clang-format: update c/cc coding style
Update all cc code using .clang-format; please do so as well for future PRs etc.; emacs has a handy 'clang-format' mode to make this automatic. For comparing old changes with git blame, we can disregard this one using --ignore-rev (see https://www.moxio.com/blog/43/ignoring-bulk-change-commits-with-git-blame )
This commit is contained in:
@ -29,163 +29,166 @@ namespace Mu {
|
||||
|
||||
constexpr std::size_t UnlimitedAsyncQueueSize{0};
|
||||
|
||||
template <typename ItemType, /**< the type of Item to queue */
|
||||
template <typename ItemType, /**< the type of Item to queue */
|
||||
std::size_t MaxSize = UnlimitedAsyncQueueSize, /**< maximum size for the queue */
|
||||
typename Allocator = std::allocator<ItemType>> /**< allocator the items */
|
||||
|
||||
class AsyncQueue {
|
||||
public:
|
||||
using value_type = ItemType;
|
||||
using allocator_type = Allocator;
|
||||
using size_type = std::size_t;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using pointer = typename std::allocator_traits<allocator_type>::pointer;
|
||||
using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
|
||||
public:
|
||||
using value_type = ItemType;
|
||||
using allocator_type = Allocator;
|
||||
using size_type = std::size_t;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using pointer = typename std::allocator_traits<allocator_type>::pointer;
|
||||
using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
|
||||
|
||||
using Timeout = std::chrono::steady_clock::duration;
|
||||
using Timeout = std::chrono::steady_clock::duration;
|
||||
|
||||
#define LOCKED std::unique_lock<std::mutex> lock(m_);
|
||||
#define LOCKED std::unique_lock<std::mutex> lock(m_);
|
||||
|
||||
bool push (const value_type& item, Timeout timeout = {}) {
|
||||
return push(std::move(value_type(item)));
|
||||
}
|
||||
bool push(const value_type& item, Timeout timeout = {})
|
||||
{
|
||||
return push(std::move(value_type(item)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an item to the end of the queue by moving it
|
||||
*
|
||||
* @param item the item to move to the end of the queue
|
||||
* @param timeout and optional timeout
|
||||
*
|
||||
* @return true if the item was pushed; false otherwise.
|
||||
*/
|
||||
bool push (value_type&& item, Timeout timeout = {}) {
|
||||
/**
|
||||
* Push an item to the end of the queue by moving it
|
||||
*
|
||||
* @param item the item to move to the end of the queue
|
||||
* @param timeout and optional timeout
|
||||
*
|
||||
* @return true if the item was pushed; false otherwise.
|
||||
*/
|
||||
bool push(value_type&& item, Timeout timeout = {})
|
||||
{
|
||||
LOCKED;
|
||||
|
||||
LOCKED;
|
||||
if (!unlimited()) {
|
||||
const auto rv = cv_full_.wait_for(lock, timeout, [&]() {
|
||||
return !full_unlocked();
|
||||
}) && !full_unlocked();
|
||||
if (!rv)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!unlimited()) {
|
||||
const auto rv = cv_full_.wait_for(lock, timeout,[&](){
|
||||
return !full_unlocked();}) && !full_unlocked();
|
||||
if (!rv)
|
||||
return false;
|
||||
}
|
||||
q_.emplace_back(std::move(item));
|
||||
lock.unlock();
|
||||
|
||||
q_.emplace_back(std::move(item));
|
||||
lock.unlock();
|
||||
cv_empty_.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
cv_empty_.notify_one();
|
||||
return true;
|
||||
/**
|
||||
* Pop an item from the queue
|
||||
*
|
||||
* @param receives the value if the function returns true
|
||||
* @param timeout optional time to wait for an item to become available
|
||||
*
|
||||
* @return true if an item was popped (into val), false otherwise.
|
||||
*/
|
||||
bool pop(value_type& val, Timeout timeout = {})
|
||||
{
|
||||
LOCKED;
|
||||
|
||||
}
|
||||
if (timeout != Timeout{}) {
|
||||
const auto rv = cv_empty_.wait_for(lock, timeout, [&]() {
|
||||
return !q_.empty();
|
||||
}) && !q_.empty();
|
||||
if (!rv)
|
||||
return false;
|
||||
|
||||
/**
|
||||
* Pop an item from the queue
|
||||
*
|
||||
* @param receives the value if the function returns true
|
||||
* @param timeout optional time to wait for an item to become available
|
||||
*
|
||||
* @return true if an item was popped (into val), false otherwise.
|
||||
*/
|
||||
bool pop (value_type& val, Timeout timeout = {}) {
|
||||
} else if (q_.empty())
|
||||
return false;
|
||||
|
||||
LOCKED;
|
||||
val = std::move(q_.front());
|
||||
q_.pop_front();
|
||||
lock.unlock();
|
||||
cv_full_.notify_one();
|
||||
|
||||
if (timeout != Timeout{}) {
|
||||
const auto rv = cv_empty_.wait_for(lock, timeout,[&](){
|
||||
return !q_.empty(); }) && !q_.empty();
|
||||
if (!rv)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if (q_.empty())
|
||||
return false;
|
||||
/**
|
||||
* Clear the queue
|
||||
*
|
||||
*/
|
||||
void clear()
|
||||
{
|
||||
LOCKED;
|
||||
q_.clear();
|
||||
lock.unlock();
|
||||
cv_full_.notify_one();
|
||||
}
|
||||
|
||||
val = std::move(q_.front());
|
||||
q_.pop_front();
|
||||
lock.unlock();
|
||||
cv_full_.notify_one();
|
||||
/**
|
||||
* Size of the queue
|
||||
*
|
||||
*
|
||||
* @return the size
|
||||
*/
|
||||
size_type size() const
|
||||
{
|
||||
LOCKED;
|
||||
return q_.size();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Maximum size of the queue if specified through the template
|
||||
* parameter; otherwise the (theoretical) max_size of the inner
|
||||
* container.
|
||||
*
|
||||
* @return the maximum size
|
||||
*/
|
||||
size_type max_size() const
|
||||
{
|
||||
if (unlimited())
|
||||
return q_.max_size();
|
||||
else
|
||||
return MaxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the queue
|
||||
*
|
||||
*/
|
||||
void clear() {
|
||||
LOCKED;
|
||||
q_.clear();
|
||||
lock.unlock();
|
||||
cv_full_.notify_one();
|
||||
}
|
||||
/**
|
||||
* Is the queue empty?
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
bool empty() const
|
||||
{
|
||||
LOCKED;
|
||||
return q_.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Size of the queue
|
||||
*
|
||||
*
|
||||
* @return the size
|
||||
*/
|
||||
size_type size() const {
|
||||
LOCKED;
|
||||
return q_.size();
|
||||
}
|
||||
/**
|
||||
* Is the queue full? Returns false unless a maximum size was specified
|
||||
* (as a template argument)
|
||||
*
|
||||
* @return true or false.
|
||||
*/
|
||||
bool full() const
|
||||
{
|
||||
if (unlimited())
|
||||
return false;
|
||||
|
||||
/**
|
||||
* Maximum size of the queue if specified through the template
|
||||
* parameter; otherwise the (theoretical) max_size of the inner
|
||||
* container.
|
||||
*
|
||||
* @return the maximum size
|
||||
*/
|
||||
size_type max_size() const {
|
||||
if (unlimited())
|
||||
return q_.max_size();
|
||||
else
|
||||
return MaxSize;
|
||||
}
|
||||
LOCKED;
|
||||
return full_unlocked();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the queue empty?
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
bool empty() const {
|
||||
LOCKED;
|
||||
return q_.empty();
|
||||
}
|
||||
/**
|
||||
* Is this queue (theoretically) unlimited in size?
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
constexpr static bool unlimited() { return MaxSize == UnlimitedAsyncQueueSize; }
|
||||
|
||||
/**
|
||||
* Is the queue full? Returns false unless a maximum size was specified
|
||||
* (as a template argument)
|
||||
*
|
||||
* @return true or false.
|
||||
*/
|
||||
bool full() const {
|
||||
if (unlimited())
|
||||
return false;
|
||||
private:
|
||||
bool full_unlocked() const { return q_.size() >= max_size(); }
|
||||
|
||||
LOCKED;
|
||||
return full_unlocked();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this queue (theoretically) unlimited in size?
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
constexpr static bool unlimited() {
|
||||
return MaxSize == UnlimitedAsyncQueueSize;
|
||||
}
|
||||
|
||||
private:
|
||||
bool full_unlocked() const {
|
||||
return q_.size() >= max_size();
|
||||
}
|
||||
|
||||
std::deque<ItemType, Allocator> q_;
|
||||
mutable std::mutex m_;
|
||||
std::condition_variable cv_full_, cv_empty_;
|
||||
std::deque<ItemType, Allocator> q_;
|
||||
mutable std::mutex m_;
|
||||
std::condition_variable cv_full_, cv_empty_;
|
||||
};
|
||||
|
||||
} // namespace mu
|
||||
} // namespace Mu
|
||||
|
||||
#endif /* __MU_ASYNC_QUEUE_HH__ */
|
||||
|
||||
@ -30,170 +30,165 @@ using namespace Command;
|
||||
void
|
||||
Command::invoke(const Command::CommandMap& cmap, const Sexp& call)
|
||||
{
|
||||
if (!call.is_call()) {
|
||||
throw Mu::Error{Error::Code::Command,
|
||||
"expected call-sexpr but got %s",
|
||||
call.to_sexp_string().c_str()};
|
||||
}
|
||||
if (!call.is_call()) {
|
||||
throw Mu::Error{Error::Code::Command,
|
||||
"expected call-sexpr but got %s",
|
||||
call.to_sexp_string().c_str()};
|
||||
}
|
||||
|
||||
const auto& params{call.list()};
|
||||
const auto cmd_it = cmap.find(params.at(0).value());
|
||||
if (cmd_it == cmap.end())
|
||||
throw Mu::Error{Error::Code::Command,
|
||||
"unknown command in call %s",
|
||||
call.to_sexp_string().c_str()};
|
||||
const auto& params{call.list()};
|
||||
const auto cmd_it = cmap.find(params.at(0).value());
|
||||
if (cmd_it == cmap.end())
|
||||
throw Mu::Error{Error::Code::Command,
|
||||
"unknown command in call %s",
|
||||
call.to_sexp_string().c_str()};
|
||||
|
||||
const auto& cinfo{cmd_it->second};
|
||||
const auto& cinfo{cmd_it->second};
|
||||
|
||||
// all required parameters must be present
|
||||
for (auto&& arg: cinfo.args) {
|
||||
const auto& argname{arg.first};
|
||||
const auto& arginfo{arg.second};
|
||||
// all required parameters must be present
|
||||
for (auto&& arg : cinfo.args) {
|
||||
const auto& argname{arg.first};
|
||||
const auto& arginfo{arg.second};
|
||||
|
||||
// calls used keyword-parameters, e.g.
|
||||
// (my-function :bar 1 :cuux "fnorb")
|
||||
// so, we're looking for the odd-numbered parameters.
|
||||
const auto param_it = [&]()->Sexp::Seq::const_iterator {
|
||||
for (size_t i = 1; i < params.size(); i += 2)
|
||||
if (params.at(i).is_symbol() && params.at(i).value() == argname)
|
||||
return params.begin() + i + 1;
|
||||
// calls used keyword-parameters, e.g.
|
||||
// (my-function :bar 1 :cuux "fnorb")
|
||||
// so, we're looking for the odd-numbered parameters.
|
||||
const auto param_it = [&]() -> Sexp::Seq::const_iterator {
|
||||
for (size_t i = 1; i < params.size(); i += 2)
|
||||
if (params.at(i).is_symbol() && params.at(i).value() == argname)
|
||||
return params.begin() + i + 1;
|
||||
|
||||
return params.end();
|
||||
return params.end();
|
||||
}();
|
||||
|
||||
}();
|
||||
// it's an error when a required parameter is missing.
|
||||
if (param_it == params.end()) {
|
||||
if (arginfo.required)
|
||||
throw Mu::Error{Error::Code::Command,
|
||||
"missing required parameter %s in call %s",
|
||||
argname.c_str(),
|
||||
call.to_sexp_string().c_str()};
|
||||
continue; // not required
|
||||
}
|
||||
|
||||
// it's an error when a required parameter is missing.
|
||||
if (param_it == params.end()) {
|
||||
if (arginfo.required)
|
||||
throw Mu::Error{Error::Code::Command,
|
||||
"missing required parameter %s in call %s",
|
||||
argname.c_str(), call.to_sexp_string().c_str()};
|
||||
continue; // not required
|
||||
}
|
||||
// the types must match, but the 'nil' symbol is acceptable as
|
||||
// "no value"
|
||||
if (param_it->type() != arginfo.type && !(param_it->is_nil()))
|
||||
throw Mu::Error{Error::Code::Command,
|
||||
"parameter %s expects type %s, but got %s in call %s",
|
||||
argname.c_str(),
|
||||
to_string(arginfo.type).c_str(),
|
||||
to_string(param_it->type()).c_str(),
|
||||
call.to_sexp_string().c_str()};
|
||||
}
|
||||
|
||||
// the types must match, but the 'nil' symbol is acceptable as
|
||||
// "no value"
|
||||
if (param_it->type() != arginfo.type && !(param_it->is_nil()))
|
||||
throw Mu::Error{Error::Code::Command,
|
||||
"parameter %s expects type %s, but got %s in call %s",
|
||||
argname.c_str(), to_string(arginfo.type).c_str(),
|
||||
to_string(param_it->type()).c_str(),
|
||||
call.to_sexp_string().c_str()};
|
||||
}
|
||||
// all passed parameters must be known
|
||||
for (size_t i = 1; i < params.size(); i += 2) {
|
||||
if (std::none_of(cinfo.args.begin(), cinfo.args.end(), [&](auto&& arg) {
|
||||
return params.at(i).value() == arg.first;
|
||||
}))
|
||||
throw Mu::Error{Error::Code::Command,
|
||||
"unknown parameter %s in call %s",
|
||||
params.at(i).value().c_str(),
|
||||
call.to_sexp_string().c_str()};
|
||||
}
|
||||
|
||||
// all passed parameters must be known
|
||||
for (size_t i = 1; i < params.size(); i += 2) {
|
||||
if (std::none_of(cinfo.args.begin(), cinfo.args.end(),
|
||||
[&](auto&& arg) {return params.at(i).value() == arg.first;}))
|
||||
throw Mu::Error{Error::Code::Command,
|
||||
"unknown parameter %s in call %s",
|
||||
params.at(i).value().c_str(), call.to_sexp_string().c_str()};
|
||||
}
|
||||
|
||||
if (cinfo.handler)
|
||||
cinfo.handler(params);
|
||||
if (cinfo.handler)
|
||||
cinfo.handler(params);
|
||||
}
|
||||
|
||||
static Sexp::Seq::const_iterator
|
||||
find_param_node (const Parameters& params, const std::string& argname)
|
||||
find_param_node(const Parameters& params, const std::string& argname)
|
||||
{
|
||||
if (params.empty())
|
||||
throw Error(Error::Code::InvalidArgument,
|
||||
"params must not be empty");
|
||||
if (params.empty())
|
||||
throw Error(Error::Code::InvalidArgument, "params must not be empty");
|
||||
|
||||
if (argname.empty() || argname.at(0) != ':')
|
||||
throw Error(Error::Code::InvalidArgument,
|
||||
"property key must start with ':' but got '%s')",
|
||||
argname.c_str());
|
||||
if (argname.empty() || argname.at(0) != ':')
|
||||
throw Error(Error::Code::InvalidArgument,
|
||||
"property key must start with ':' but got '%s')",
|
||||
argname.c_str());
|
||||
|
||||
for (size_t i = 1; i < params.size(); i += 2) {
|
||||
if (i + 1 != params.size() &&
|
||||
params.at(i).is_symbol() && params.at(i).value() == argname)
|
||||
return params.begin() + i + 1;
|
||||
}
|
||||
for (size_t i = 1; i < params.size(); i += 2) {
|
||||
if (i + 1 != params.size() && params.at(i).is_symbol() &&
|
||||
params.at(i).value() == argname)
|
||||
return params.begin() + i + 1;
|
||||
}
|
||||
|
||||
return params.end();
|
||||
return params.end();
|
||||
}
|
||||
|
||||
|
||||
static Error
|
||||
wrong_type (Sexp::Type expected, Sexp::Type got)
|
||||
wrong_type(Sexp::Type expected, Sexp::Type got)
|
||||
{
|
||||
return Error(Error::Code::InvalidArgument,
|
||||
"expected <%s> but got <%s>",
|
||||
to_string(expected).c_str(),
|
||||
to_string(got).c_str());
|
||||
}
|
||||
|
||||
|
||||
const std::string&
|
||||
Command::get_string_or (const Parameters& params, const std::string& argname,
|
||||
const std::string& alt)
|
||||
{
|
||||
const auto it = find_param_node (params, argname);
|
||||
if (it == params.end() || it->is_nil())
|
||||
return alt;
|
||||
else if (!it->is_string())
|
||||
throw wrong_type(Sexp::Type::String, it->type());
|
||||
else
|
||||
return it->value();
|
||||
return Error(Error::Code::InvalidArgument,
|
||||
"expected <%s> but got <%s>",
|
||||
to_string(expected).c_str(),
|
||||
to_string(got).c_str());
|
||||
}
|
||||
|
||||
const std::string&
|
||||
Command::get_symbol_or (const Parameters& params, const std::string& argname,
|
||||
const std::string& alt)
|
||||
Command::get_string_or(const Parameters& params, const std::string& argname, const std::string& alt)
|
||||
{
|
||||
const auto it = find_param_node (params, argname);
|
||||
if (it == params.end() || it->is_nil())
|
||||
return alt;
|
||||
else if (!it->is_symbol())
|
||||
throw wrong_type(Sexp::Type::Symbol, it->type());
|
||||
else
|
||||
return it->value();
|
||||
const auto it = find_param_node(params, argname);
|
||||
if (it == params.end() || it->is_nil())
|
||||
return alt;
|
||||
else if (!it->is_string())
|
||||
throw wrong_type(Sexp::Type::String, it->type());
|
||||
else
|
||||
return it->value();
|
||||
}
|
||||
|
||||
const std::string&
|
||||
Command::get_symbol_or(const Parameters& params, const std::string& argname, const std::string& alt)
|
||||
{
|
||||
const auto it = find_param_node(params, argname);
|
||||
if (it == params.end() || it->is_nil())
|
||||
return alt;
|
||||
else if (!it->is_symbol())
|
||||
throw wrong_type(Sexp::Type::Symbol, it->type());
|
||||
else
|
||||
return it->value();
|
||||
}
|
||||
|
||||
int
|
||||
Command::get_int_or (const Parameters& params, const std::string& argname,
|
||||
int alt)
|
||||
Command::get_int_or(const Parameters& params, const std::string& argname, int alt)
|
||||
{
|
||||
const auto it = find_param_node (params, argname);
|
||||
if (it == params.end() || it->is_nil())
|
||||
return alt;
|
||||
else if (!it->is_number())
|
||||
throw wrong_type(Sexp::Type::Number, it->type());
|
||||
else
|
||||
return ::atoi(it->value().c_str());
|
||||
const auto it = find_param_node(params, argname);
|
||||
if (it == params.end() || it->is_nil())
|
||||
return alt;
|
||||
else if (!it->is_number())
|
||||
throw wrong_type(Sexp::Type::Number, it->type());
|
||||
else
|
||||
return ::atoi(it->value().c_str());
|
||||
}
|
||||
|
||||
bool
|
||||
Command::get_bool_or (const Parameters& params, const std::string& argname,
|
||||
bool alt)
|
||||
Command::get_bool_or(const Parameters& params, const std::string& argname, bool alt)
|
||||
{
|
||||
const auto it = find_param_node (params, argname);
|
||||
if (it == params.end())
|
||||
return alt;
|
||||
else if (!it->is_symbol())
|
||||
throw wrong_type(Sexp::Type::Symbol, it->type());
|
||||
else
|
||||
return it->is_nil() ? false : true;
|
||||
const auto it = find_param_node(params, argname);
|
||||
if (it == params.end())
|
||||
return alt;
|
||||
else if (!it->is_symbol())
|
||||
throw wrong_type(Sexp::Type::Symbol, it->type());
|
||||
else
|
||||
return it->is_nil() ? false : true;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
Command::get_string_vec (const Parameters& params, const std::string& argname)
|
||||
Command::get_string_vec(const Parameters& params, const std::string& argname)
|
||||
{
|
||||
const auto it = find_param_node (params, argname);
|
||||
if (it == params.end() || it->is_nil())
|
||||
return {};
|
||||
else if (!it->is_list())
|
||||
throw wrong_type(Sexp::Type::List, it->type());
|
||||
const auto it = find_param_node(params, argname);
|
||||
if (it == params.end() || it->is_nil())
|
||||
return {};
|
||||
else if (!it->is_list())
|
||||
throw wrong_type(Sexp::Type::List, it->type());
|
||||
|
||||
std::vector<std::string> vec;
|
||||
for (const auto& n: it->list()) {
|
||||
if (!n.is_string())
|
||||
throw wrong_type(Sexp::Type::String, n.type());
|
||||
vec.emplace_back (n.value());
|
||||
}
|
||||
std::vector<std::string> vec;
|
||||
for (const auto& n : it->list()) {
|
||||
if (!n.is_string())
|
||||
throw wrong_type(Sexp::Type::String, n.type());
|
||||
vec.emplace_back(n.value());
|
||||
}
|
||||
|
||||
return vec;
|
||||
return vec;
|
||||
}
|
||||
|
||||
@ -30,7 +30,6 @@
|
||||
#include "utils/mu-error.hh"
|
||||
#include "utils/mu-sexp.hh"
|
||||
|
||||
|
||||
namespace Mu {
|
||||
namespace Command {
|
||||
|
||||
@ -46,63 +45,66 @@ namespace Command {
|
||||
/// for specify a non-required parameter to be absent; this is for convenience on the
|
||||
/// call side.
|
||||
|
||||
|
||||
/// Information about a function argument
|
||||
struct ArgInfo {
|
||||
ArgInfo (Sexp::Type typearg, bool requiredarg, std::string&& docarg):
|
||||
type{typearg}, required{requiredarg},docstring{std::move(docarg)}
|
||||
{}
|
||||
const Sexp::Type type; /**< Sexp::Type of the argument */
|
||||
const bool required; /**< Is this argument required? */
|
||||
const std::string docstring; /**< Documentation */
|
||||
ArgInfo(Sexp::Type typearg, bool requiredarg, std::string&& docarg)
|
||||
: type{typearg}, required{requiredarg}, docstring{std::move(docarg)}
|
||||
{
|
||||
}
|
||||
const Sexp::Type type; /**< Sexp::Type of the argument */
|
||||
const bool required; /**< Is this argument required? */
|
||||
const std::string docstring; /**< Documentation */
|
||||
};
|
||||
|
||||
/// The arguments for a function, which maps their names to the information.
|
||||
using ArgMap = std::unordered_map<std::string, ArgInfo>;
|
||||
using ArgMap = std::unordered_map<std::string, ArgInfo>;
|
||||
// The parameters to a Handler.
|
||||
using Parameters = Sexp::Seq;
|
||||
|
||||
int get_int_or (const Parameters& parms, const std::string& argname, int alt=0);
|
||||
bool get_bool_or (const Parameters& parms, const std::string& argname, bool alt=false);
|
||||
const std::string& get_string_or (const Parameters& parms, const std::string& argname, const std::string& alt="");
|
||||
const std::string& get_symbol_or (const Parameters& parms, const std::string& argname, const std::string& alt="nil");
|
||||
|
||||
|
||||
std::vector<std::string> get_string_vec (const Parameters& params, const std::string& argname);
|
||||
int get_int_or(const Parameters& parms, const std::string& argname, int alt = 0);
|
||||
bool get_bool_or(const Parameters& parms, const std::string& argname, bool alt = false);
|
||||
const std::string&
|
||||
get_string_or(const Parameters& parms, const std::string& argname, const std::string& alt = "");
|
||||
const std::string&
|
||||
get_symbol_or(const Parameters& parms, const std::string& argname, const std::string& alt = "nil");
|
||||
|
||||
std::vector<std::string> get_string_vec(const Parameters& params, const std::string& argname);
|
||||
|
||||
// A handler function
|
||||
using Handler = std::function<void(const Parameters&)>;
|
||||
using Handler = std::function<void(const Parameters&)>;
|
||||
|
||||
/// Information about some command
|
||||
struct CommandInfo {
|
||||
CommandInfo(ArgMap&& argmaparg, std::string&& docarg, Handler&& handlerarg):
|
||||
args{std::move(argmaparg)}, docstring{std::move(docarg)}, handler{std::move(handlerarg)}
|
||||
{}
|
||||
const ArgMap args;
|
||||
const std::string docstring;
|
||||
const Handler handler;
|
||||
CommandInfo(ArgMap&& argmaparg, std::string&& docarg, Handler&& handlerarg)
|
||||
: args{std::move(argmaparg)}, docstring{std::move(docarg)}, handler{
|
||||
std::move(handlerarg)}
|
||||
{
|
||||
}
|
||||
const ArgMap args;
|
||||
const std::string docstring;
|
||||
const Handler handler;
|
||||
|
||||
/**
|
||||
* Get a sorted list of argument names, for display. Required args come
|
||||
* first, then alphabetical.
|
||||
*
|
||||
* @return vec with the sorted names.
|
||||
*/
|
||||
std::vector<std::string> sorted_argnames() const { // sort args -- by required, then alphabetical.
|
||||
std::vector<std::string> names;
|
||||
for (auto&& arg: args)
|
||||
names.emplace_back(arg.first);
|
||||
std::sort(names.begin(), names.end(), [&](const auto& name1, const auto& name2) {
|
||||
const auto& arg1{args.find(name1)->second};
|
||||
const auto& arg2{args.find(name2)->second};
|
||||
if (arg1.required != arg2.required)
|
||||
return arg1.required;
|
||||
else
|
||||
return name1 < name2;
|
||||
});
|
||||
return names;
|
||||
}
|
||||
/**
|
||||
* Get a sorted list of argument names, for display. Required args come
|
||||
* first, then alphabetical.
|
||||
*
|
||||
* @return vec with the sorted names.
|
||||
*/
|
||||
std::vector<std::string> sorted_argnames() const
|
||||
{ // sort args -- by required, then alphabetical.
|
||||
std::vector<std::string> names;
|
||||
for (auto&& arg : args)
|
||||
names.emplace_back(arg.first);
|
||||
std::sort(names.begin(), names.end(), [&](const auto& name1, const auto& name2) {
|
||||
const auto& arg1{args.find(name1)->second};
|
||||
const auto& arg2{args.find(name2)->second};
|
||||
if (arg1.required != arg2.required)
|
||||
return arg1.required;
|
||||
else
|
||||
return name1 < name2;
|
||||
});
|
||||
return names;
|
||||
}
|
||||
};
|
||||
/// All commands, mapping their name to information about them.
|
||||
using CommandMap = std::unordered_map<std::string, CommandInfo>;
|
||||
@ -120,37 +122,34 @@ using CommandMap = std::unordered_map<std::string, CommandInfo>;
|
||||
*/
|
||||
void invoke(const Command::CommandMap& cmap, const Sexp& call);
|
||||
|
||||
|
||||
static inline std::ostream&
|
||||
operator<<(std::ostream& os, const Command::ArgInfo& info)
|
||||
{
|
||||
os << info.type
|
||||
<< " (" << ( info.required ? "required" : "optional" ) << ")";
|
||||
os << info.type << " (" << (info.required ? "required" : "optional") << ")";
|
||||
|
||||
return os;
|
||||
return os;
|
||||
}
|
||||
|
||||
static inline std::ostream&
|
||||
operator<<(std::ostream& os, const Command::CommandInfo& info)
|
||||
{
|
||||
for (auto&& arg: info.args)
|
||||
os << " " << arg.first << " " << arg.second << '\n'
|
||||
<< " " << arg.second.docstring << "\n";
|
||||
for (auto&& arg : info.args)
|
||||
os << " " << arg.first << " " << arg.second << '\n'
|
||||
<< " " << arg.second.docstring << "\n";
|
||||
|
||||
return os;
|
||||
return os;
|
||||
}
|
||||
|
||||
static inline std::ostream&
|
||||
operator<<(std::ostream& os, const Command::CommandMap& map)
|
||||
{
|
||||
for (auto&& c: map)
|
||||
os << c.first << '\n' << c.second;
|
||||
for (auto&& c : map)
|
||||
os << c.first << '\n' << c.second;
|
||||
|
||||
return os;
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace Command
|
||||
} // namespace Mu
|
||||
|
||||
|
||||
#endif /* MU_COMMAND_PARSER_HH__ */
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MU_ERROR_HH__
|
||||
#define MU_ERROR_HH__
|
||||
|
||||
@ -27,106 +26,101 @@
|
||||
|
||||
namespace Mu {
|
||||
|
||||
struct Error final: public std::exception {
|
||||
struct Error final : public std::exception {
|
||||
enum struct Code {
|
||||
AccessDenied = 100, // don't overlap with MuError
|
||||
Command,
|
||||
File,
|
||||
Index,
|
||||
Internal,
|
||||
InvalidArgument,
|
||||
Message,
|
||||
NotFound,
|
||||
Parsing,
|
||||
Query,
|
||||
SchemaMismatch,
|
||||
Store,
|
||||
};
|
||||
|
||||
enum struct Code {
|
||||
AccessDenied = 100, // don't overlap with MuError
|
||||
Command,
|
||||
File,
|
||||
Index,
|
||||
Internal,
|
||||
InvalidArgument,
|
||||
Message,
|
||||
NotFound,
|
||||
Parsing,
|
||||
Query,
|
||||
SchemaMismatch,
|
||||
Store,
|
||||
};
|
||||
/**
|
||||
* Construct an error
|
||||
*
|
||||
* @param codearg error-code
|
||||
* #param msgarg the error diecription
|
||||
*/
|
||||
Error(Code codearg, const std::string& msgarg) : code_{codearg}, what_{msgarg} {}
|
||||
|
||||
/**
|
||||
* Construct an error
|
||||
*
|
||||
* @param codearg error-code
|
||||
* #param msgarg the error diecription
|
||||
*/
|
||||
Error(Code codearg, const std::string& msgarg):
|
||||
code_{codearg}, what_{msgarg}
|
||||
{}
|
||||
/**
|
||||
* Build an error from an error-code and a format string
|
||||
*
|
||||
* @param code error-code
|
||||
* @param frm format string
|
||||
* @param ... format parameters
|
||||
*
|
||||
* @return an Error object
|
||||
*/
|
||||
__attribute__((format(printf, 3, 0))) Error(Code codearg, const char* frm, ...)
|
||||
: code_{codearg}
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, frm);
|
||||
what_ = vformat(frm, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an error from an error-code and a format string
|
||||
*
|
||||
* @param code error-code
|
||||
* @param frm format string
|
||||
* @param ... format parameters
|
||||
*
|
||||
* @return an Error object
|
||||
*/
|
||||
__attribute__((format(printf, 3, 0)))
|
||||
Error(Code codearg, const char *frm, ...): code_{codearg} {
|
||||
va_list args;
|
||||
va_start(args, frm);
|
||||
what_ = vformat(frm, args);
|
||||
va_end(args);
|
||||
}
|
||||
Error(Error&& rhs) = default;
|
||||
Error(const Error& rhs) = delete;
|
||||
|
||||
Error(Error&& rhs) = default;
|
||||
Error(const Error& rhs) = delete;
|
||||
/**
|
||||
* Build an error from a GError an error-code and a format string
|
||||
*
|
||||
* @param code error-code
|
||||
* @param gerr a GError or {}, which is consumed
|
||||
* @param frm format string
|
||||
* @param ... format parameters
|
||||
*
|
||||
* @return an Error object
|
||||
*/
|
||||
__attribute__((format(printf, 4, 0)))
|
||||
Error(Code codearg, GError** err, const char* frm, ...)
|
||||
: code_{codearg}
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, frm);
|
||||
what_ = vformat(frm, args);
|
||||
va_end(args);
|
||||
|
||||
/**
|
||||
* Build an error from a GError an error-code and a format string
|
||||
*
|
||||
* @param code error-code
|
||||
* @param gerr a GError or {}, which is consumed
|
||||
* @param frm format string
|
||||
* @param ... format parameters
|
||||
*
|
||||
* @return an Error object
|
||||
*/
|
||||
__attribute__((format(printf, 4, 0)))
|
||||
Error(Code codearg, GError **err, const char *frm, ...): code_{codearg} {
|
||||
if (err && *err)
|
||||
what_ += format(": %s", (*err)->message);
|
||||
else
|
||||
what_ += ": something went wrong";
|
||||
|
||||
va_list args;
|
||||
va_start(args, frm);
|
||||
what_ = vformat(frm, args);
|
||||
va_end(args);
|
||||
g_clear_error(err);
|
||||
}
|
||||
|
||||
if (err && *err)
|
||||
what_ += format (": %s", (*err)->message);
|
||||
else
|
||||
what_ += ": something went wrong";
|
||||
/**
|
||||
* DTOR
|
||||
*
|
||||
*/
|
||||
virtual ~Error() = default;
|
||||
|
||||
g_clear_error(err);
|
||||
}
|
||||
/**
|
||||
* Get the descriptiove message.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual const char* what() const noexcept override { return what_.c_str(); }
|
||||
|
||||
/**
|
||||
* DTOR
|
||||
*
|
||||
*/
|
||||
virtual ~Error() = default;
|
||||
/**
|
||||
* Get the error-code for this error
|
||||
*
|
||||
* @return the error-code
|
||||
*/
|
||||
Code code() const { return code_; }
|
||||
|
||||
/**
|
||||
* Get the descriptiove message.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual const char* what() const noexcept override {
|
||||
return what_.c_str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error-code for this error
|
||||
*
|
||||
* @return the error-code
|
||||
*/
|
||||
Code code() const { return code_; }
|
||||
|
||||
|
||||
|
||||
private:
|
||||
const Code code_;
|
||||
std::string what_;
|
||||
private:
|
||||
const Code code_;
|
||||
std::string what_;
|
||||
};
|
||||
|
||||
} // namespace Mu
|
||||
|
||||
@ -35,151 +35,145 @@ using namespace Mu;
|
||||
static bool MuLogInitialized = false;
|
||||
static Mu::LogOptions MuLogOptions;
|
||||
static std::ofstream MuStream;
|
||||
static auto MaxLogFileSize = 1000 * 1024;
|
||||
static auto MaxLogFileSize = 1000 * 1024;
|
||||
|
||||
static std::string MuLogPath;
|
||||
static std::string MuLogPath;
|
||||
|
||||
static bool
|
||||
maybe_open_logfile ()
|
||||
maybe_open_logfile()
|
||||
{
|
||||
if (MuStream.is_open())
|
||||
return true;
|
||||
if (MuStream.is_open())
|
||||
return true;
|
||||
|
||||
MuStream.open (MuLogPath, std::ios::out | std::ios::app );
|
||||
if (!MuStream.is_open()) {
|
||||
std::cerr << "opening " << MuLogPath << " failed:"
|
||||
<< g_strerror(errno) << std::endl;
|
||||
return false;
|
||||
}
|
||||
MuStream.open(MuLogPath, std::ios::out | std::ios::app);
|
||||
if (!MuStream.is_open()) {
|
||||
std::cerr << "opening " << MuLogPath << " failed:" << g_strerror(errno)
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
MuStream.sync_with_stdio(false);
|
||||
return true;
|
||||
MuStream.sync_with_stdio(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
maybe_rotate_logfile ()
|
||||
maybe_rotate_logfile()
|
||||
{
|
||||
static unsigned n = 0;
|
||||
static unsigned n = 0;
|
||||
|
||||
if (n++ % 1000 != 0)
|
||||
return true;
|
||||
if (n++ % 1000 != 0)
|
||||
return true;
|
||||
|
||||
GStatBuf statbuf;
|
||||
if (g_stat(MuLogPath.c_str(), &statbuf) == -1 ||
|
||||
statbuf.st_size <= MaxLogFileSize)
|
||||
return true;
|
||||
GStatBuf statbuf;
|
||||
if (g_stat(MuLogPath.c_str(), &statbuf) == -1 || statbuf.st_size <= MaxLogFileSize)
|
||||
return true;
|
||||
|
||||
const auto old = MuLogPath + ".old";
|
||||
g_unlink(old.c_str()); // opportunistic
|
||||
const auto old = MuLogPath + ".old";
|
||||
g_unlink(old.c_str()); // opportunistic
|
||||
|
||||
if (MuStream.is_open())
|
||||
MuStream.close();
|
||||
if (MuStream.is_open())
|
||||
MuStream.close();
|
||||
|
||||
if (g_rename(MuLogPath.c_str(), old.c_str()) != 0)
|
||||
std::cerr << "failed to rename "
|
||||
<< MuLogPath << " -> " << old.c_str()
|
||||
<< ": " << g_strerror(errno) << std::endl;
|
||||
if (g_rename(MuLogPath.c_str(), old.c_str()) != 0)
|
||||
std::cerr << "failed to rename " << MuLogPath << " -> " << old.c_str() << ": "
|
||||
<< g_strerror(errno) << std::endl;
|
||||
|
||||
return maybe_open_logfile();
|
||||
return maybe_open_logfile();
|
||||
}
|
||||
|
||||
static GLogWriterOutput
|
||||
log_file (GLogLevelFlags level, const GLogField *fields, gsize n_fields,
|
||||
gpointer user_data)
|
||||
log_file(GLogLevelFlags level, const GLogField* fields, gsize n_fields, gpointer user_data)
|
||||
{
|
||||
if (!maybe_open_logfile())
|
||||
return G_LOG_WRITER_UNHANDLED;
|
||||
if (!maybe_open_logfile())
|
||||
return G_LOG_WRITER_UNHANDLED;
|
||||
|
||||
char timebuf[22];
|
||||
time_t now{::time(NULL)};
|
||||
::strftime (timebuf, sizeof(timebuf), "%F %T", ::localtime(&now));
|
||||
char timebuf[22];
|
||||
time_t now{::time(NULL)};
|
||||
::strftime(timebuf, sizeof(timebuf), "%F %T", ::localtime(&now));
|
||||
|
||||
char *msg = g_log_writer_format_fields (level, fields, n_fields, FALSE);
|
||||
if (msg && msg[0] == '\n') // hmm... seems lines start with '\n'r
|
||||
msg[0] = ' ';
|
||||
char* msg = g_log_writer_format_fields(level, fields, n_fields, FALSE);
|
||||
if (msg && msg[0] == '\n') // hmm... seems lines start with '\n'r
|
||||
msg[0] = ' ';
|
||||
|
||||
MuStream << timebuf << ' ' << msg << std::endl;
|
||||
MuStream << timebuf << ' ' << msg << std::endl;
|
||||
|
||||
g_free (msg);
|
||||
g_free(msg);
|
||||
|
||||
return maybe_rotate_logfile() ? G_LOG_WRITER_HANDLED : G_LOG_WRITER_UNHANDLED;
|
||||
return maybe_rotate_logfile() ? G_LOG_WRITER_HANDLED : G_LOG_WRITER_UNHANDLED;
|
||||
}
|
||||
|
||||
static GLogWriterOutput
|
||||
log_stdouterr (GLogLevelFlags level, const GLogField *fields, gsize n_fields,
|
||||
gpointer user_data)
|
||||
log_stdouterr(GLogLevelFlags level, const GLogField* fields, gsize n_fields, gpointer user_data)
|
||||
{
|
||||
return g_log_writer_standard_streams (level, fields, n_fields, user_data);
|
||||
return g_log_writer_standard_streams(level, fields, n_fields, user_data);
|
||||
}
|
||||
|
||||
static GLogWriterOutput
|
||||
log_journal (GLogLevelFlags level, const GLogField *fields, gsize n_fields,
|
||||
gpointer user_data)
|
||||
log_journal(GLogLevelFlags level, const GLogField* fields, gsize n_fields, gpointer user_data)
|
||||
{
|
||||
return g_log_writer_journald (level, fields, n_fields, user_data);
|
||||
return g_log_writer_journald(level, fields, n_fields, user_data);
|
||||
}
|
||||
|
||||
void
|
||||
Mu::log_init (const std::string& path, Mu::LogOptions opts)
|
||||
Mu::log_init(const std::string& path, Mu::LogOptions opts)
|
||||
{
|
||||
if (MuLogInitialized) {
|
||||
g_error ("logging is already initialized");
|
||||
return;
|
||||
}
|
||||
if (MuLogInitialized) {
|
||||
g_error("logging is already initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
MuLogOptions = opts;
|
||||
MuLogPath = path;
|
||||
MuLogOptions = opts;
|
||||
MuLogPath = path;
|
||||
|
||||
g_log_set_writer_func (
|
||||
[](GLogLevelFlags level, const GLogField *fields, gsize n_fields,
|
||||
gpointer user_data) {
|
||||
g_log_set_writer_func(
|
||||
[](GLogLevelFlags level, const GLogField* fields, gsize n_fields, gpointer user_data) {
|
||||
// filter out debug-level messages?
|
||||
if (level == G_LOG_LEVEL_DEBUG &&
|
||||
(none_of(MuLogOptions & Mu::LogOptions::Debug)))
|
||||
return G_LOG_WRITER_HANDLED;
|
||||
|
||||
// filter out debug-level messages?
|
||||
if (level == G_LOG_LEVEL_DEBUG &&
|
||||
(none_of (MuLogOptions & Mu::LogOptions::Debug)))
|
||||
return G_LOG_WRITER_HANDLED;
|
||||
// log criticals to stdout / err or if asked
|
||||
if (level == G_LOG_LEVEL_CRITICAL ||
|
||||
any_of(MuLogOptions & Mu::LogOptions::StdOutErr)) {
|
||||
log_stdouterr(level, fields, n_fields, user_data);
|
||||
}
|
||||
|
||||
// log criticals to stdout / err or if asked
|
||||
if (level == G_LOG_LEVEL_CRITICAL ||
|
||||
any_of(MuLogOptions & Mu::LogOptions::StdOutErr)){
|
||||
log_stdouterr (level, fields, n_fields, user_data);
|
||||
}
|
||||
// log to the journal, or, if not available to a file.
|
||||
if (log_journal(level, fields, n_fields, user_data) != G_LOG_WRITER_HANDLED)
|
||||
return log_file(level, fields, n_fields, user_data);
|
||||
else
|
||||
return G_LOG_WRITER_HANDLED;
|
||||
},
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
// log to the journal, or, if not available to a file.
|
||||
if (log_journal (level, fields, n_fields, user_data) !=
|
||||
G_LOG_WRITER_HANDLED)
|
||||
return log_file (level, fields, n_fields, user_data);
|
||||
else
|
||||
return G_LOG_WRITER_HANDLED;
|
||||
}, NULL, NULL);
|
||||
g_message("logging initialized; debug: %s, stdout/stderr: %s",
|
||||
any_of(log_get_options() & LogOptions::Debug) ? "yes" : "no",
|
||||
any_of(log_get_options() & LogOptions::StdOutErr) ? "yes" : "no");
|
||||
|
||||
g_message ("logging initialized; debug: %s, stdout/stderr: %s",
|
||||
any_of(log_get_options() & LogOptions::Debug) ? "yes" : "no",
|
||||
any_of(log_get_options() & LogOptions::StdOutErr) ? "yes" : "no");
|
||||
|
||||
MuLogInitialized = true;
|
||||
MuLogInitialized = true;
|
||||
}
|
||||
|
||||
void
|
||||
Mu::log_uninit ()
|
||||
Mu::log_uninit()
|
||||
{
|
||||
if (!MuLogInitialized)
|
||||
return;
|
||||
if (!MuLogInitialized)
|
||||
return;
|
||||
|
||||
if (MuStream.is_open())
|
||||
MuStream.close();
|
||||
if (MuStream.is_open())
|
||||
MuStream.close();
|
||||
|
||||
MuLogInitialized = false;
|
||||
MuLogInitialized = false;
|
||||
}
|
||||
|
||||
void
|
||||
Mu::log_set_options (Mu::LogOptions opts)
|
||||
Mu::log_set_options(Mu::LogOptions opts)
|
||||
{
|
||||
MuLogOptions = opts;
|
||||
MuLogOptions = opts;
|
||||
}
|
||||
|
||||
Mu::LogOptions
|
||||
Mu::log_get_options ()
|
||||
Mu::log_get_options()
|
||||
{
|
||||
return MuLogOptions;
|
||||
return MuLogOptions;
|
||||
}
|
||||
|
||||
@ -30,9 +30,9 @@ namespace Mu {
|
||||
*
|
||||
*/
|
||||
enum struct LogOptions {
|
||||
None = 0, /**< Nothing specific */
|
||||
StdOutErr = 1 << 1, /**< Log to stdout/stderr */
|
||||
Debug = 1 << 2, /**< Include debug-level logs */
|
||||
None = 0, /**< Nothing specific */
|
||||
StdOutErr = 1 << 1, /**< Log to stdout/stderr */
|
||||
Debug = 1 << 2, /**< Include debug-level logs */
|
||||
};
|
||||
|
||||
/**
|
||||
@ -43,7 +43,7 @@ enum struct LogOptions {
|
||||
* @param path path to the log file
|
||||
* @param opts logging options
|
||||
*/
|
||||
void log_init (const std::string& path, LogOptions opts);
|
||||
void log_init(const std::string& path, LogOptions opts);
|
||||
|
||||
/**
|
||||
* Uninitialize the logging system
|
||||
@ -56,15 +56,14 @@ void log_uninit();
|
||||
*
|
||||
* @param opts options
|
||||
*/
|
||||
void log_set_options (LogOptions opts);
|
||||
void log_set_options(LogOptions opts);
|
||||
|
||||
/**
|
||||
* Get the current log options
|
||||
*
|
||||
* @return the log options
|
||||
*/
|
||||
LogOptions log_get_options ();
|
||||
|
||||
LogOptions log_get_options();
|
||||
|
||||
} // namespace Mu
|
||||
MU_ENABLE_BITOPS(Mu::LogOptions);
|
||||
|
||||
@ -16,14 +16,18 @@
|
||||
|
||||
#include "optional.hpp"
|
||||
|
||||
|
||||
namespace Mu {
|
||||
|
||||
/// Either a value of type T, or None
|
||||
template <typename T> using Option=tl::optional<T>;
|
||||
template <typename T> using Option = tl::optional<T>;
|
||||
|
||||
template <typename T> Option<T> Some(T&& t) { return std::move(t); }
|
||||
template <typename T>
|
||||
Option<T>
|
||||
Some(T&& t)
|
||||
{
|
||||
return std::move(t);
|
||||
}
|
||||
constexpr auto Nothing = tl::nullopt; // 'None' is take already
|
||||
|
||||
}
|
||||
} // namespace Mu
|
||||
#endif /*MU_OPTION__*/
|
||||
|
||||
@ -27,32 +27,31 @@
|
||||
#include <glib/gprintf.h>
|
||||
|
||||
#ifdef HAVE_LIBREADLINE
|
||||
# if defined(HAVE_READLINE_READLINE_H)
|
||||
# include <readline/readline.h>
|
||||
# elif defined(HAVE_READLINE_H)
|
||||
# include <readline.h>
|
||||
# else /* !defined(HAVE_READLINE_H) */
|
||||
extern char *readline ();
|
||||
# endif /* !defined(HAVE_READLINE_H) */
|
||||
char *cmdline = NULL;
|
||||
#else /* !defined(HAVE_READLINE_READLINE_H) */
|
||||
#if defined(HAVE_READLINE_READLINE_H)
|
||||
#include <readline/readline.h>
|
||||
#elif defined(HAVE_READLINE_H)
|
||||
#include <readline.h>
|
||||
#else /* !defined(HAVE_READLINE_H) */
|
||||
extern char* readline();
|
||||
#endif /* !defined(HAVE_READLINE_H) */
|
||||
char* cmdline = NULL;
|
||||
#else /* !defined(HAVE_READLINE_READLINE_H) */
|
||||
/* no readline */
|
||||
#endif /* HAVE_LIBREADLINE */
|
||||
|
||||
#ifdef HAVE_READLINE_HISTORY
|
||||
# if defined(HAVE_READLINE_HISTORY_H)
|
||||
# include <readline/history.h>
|
||||
# elif defined(HAVE_HISTORY_H)
|
||||
# include <history.h>
|
||||
# else /* !defined(HAVE_HISTORY_H) */
|
||||
extern void add_history ();
|
||||
extern int write_history ();
|
||||
extern int read_history ();
|
||||
# endif /* defined(HAVE_READLINE_HISTORY_H) */
|
||||
#if defined(HAVE_READLINE_HISTORY_H)
|
||||
#include <readline/history.h>
|
||||
#elif defined(HAVE_HISTORY_H)
|
||||
#include <history.h>
|
||||
#else /* !defined(HAVE_HISTORY_H) */
|
||||
extern void add_history();
|
||||
extern int write_history();
|
||||
extern int read_history();
|
||||
#endif /* defined(HAVE_READLINE_HISTORY_H) */
|
||||
/* no history */
|
||||
#endif /* HAVE_READLINE_HISTORY */
|
||||
|
||||
|
||||
#if defined(HAVE_LIBREADLINE) && defined(HAVE_READLINE_HISTORY)
|
||||
#define HAVE_READLINE (1)
|
||||
#else
|
||||
@ -66,66 +65,64 @@ static std::string hist_path;
|
||||
static size_t max_lines{};
|
||||
|
||||
void
|
||||
Mu::setup_readline (const std::string& histpath, size_t maxlines)
|
||||
Mu::setup_readline(const std::string& histpath, size_t maxlines)
|
||||
{
|
||||
is_a_tty = !!::isatty(::fileno(stdout));
|
||||
hist_path = histpath;
|
||||
max_lines = maxlines;
|
||||
is_a_tty = !!::isatty(::fileno(stdout));
|
||||
hist_path = histpath;
|
||||
max_lines = maxlines;
|
||||
|
||||
#if HAVE_READLINE
|
||||
rl_bind_key('\t', rl_insert); // default (filenames) is not useful
|
||||
using_history();
|
||||
read_history(hist_path.c_str());
|
||||
rl_bind_key('\t', rl_insert); // default (filenames) is not useful
|
||||
using_history();
|
||||
read_history(hist_path.c_str());
|
||||
|
||||
if (max_lines > 0)
|
||||
stifle_history(max_lines);
|
||||
if (max_lines > 0)
|
||||
stifle_history(max_lines);
|
||||
#endif /*HAVE_READLINE*/
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Mu::shutdown_readline ()
|
||||
Mu::shutdown_readline()
|
||||
{
|
||||
#if HAVE_READLINE
|
||||
if (!is_a_tty)
|
||||
return;
|
||||
if (!is_a_tty)
|
||||
return;
|
||||
|
||||
write_history(hist_path.c_str());
|
||||
if (max_lines > 0)
|
||||
history_truncate_file (hist_path.c_str(), max_lines);
|
||||
write_history(hist_path.c_str());
|
||||
if (max_lines > 0)
|
||||
history_truncate_file(hist_path.c_str(), max_lines);
|
||||
#endif /*HAVE_READLINE*/
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
Mu::read_line(bool& do_quit)
|
||||
{
|
||||
#if HAVE_READLINE
|
||||
if (is_a_tty) {
|
||||
auto buf = readline(";; mu% ");
|
||||
if (!buf) {
|
||||
do_quit = true;
|
||||
return {};
|
||||
}
|
||||
std::string line{buf};
|
||||
::free(buf);
|
||||
return line;
|
||||
}
|
||||
if (is_a_tty) {
|
||||
auto buf = readline(";; mu% ");
|
||||
if (!buf) {
|
||||
do_quit = true;
|
||||
return {};
|
||||
}
|
||||
std::string line{buf};
|
||||
::free(buf);
|
||||
return line;
|
||||
}
|
||||
#endif /*HAVE_READLINE*/
|
||||
|
||||
std::string line;
|
||||
std::cout << ";; mu> ";
|
||||
if (!std::getline(std::cin, line))
|
||||
do_quit = true;
|
||||
std::string line;
|
||||
std::cout << ";; mu> ";
|
||||
if (!std::getline(std::cin, line))
|
||||
do_quit = true;
|
||||
|
||||
return line;
|
||||
return line;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Mu::save_line(const std::string& line) {
|
||||
Mu::save_line(const std::string& line)
|
||||
{
|
||||
#if HAVE_READLINE
|
||||
if (is_a_tty)
|
||||
add_history(line.c_str());
|
||||
if (is_a_tty)
|
||||
add_history(line.c_str());
|
||||
#endif /*HAVE_READLINE*/
|
||||
}
|
||||
|
||||
@ -26,15 +26,14 @@ namespace Mu {
|
||||
* @param histpath path to the history file
|
||||
* @param max_lines maximum number of history to save
|
||||
*/
|
||||
void setup_readline(const std::string &histpath, size_t max_lines);
|
||||
void setup_readline(const std::string& histpath, size_t max_lines);
|
||||
|
||||
/**
|
||||
* Shutdown readline
|
||||
*
|
||||
*
|
||||
*/
|
||||
void shutdown_readline();
|
||||
|
||||
|
||||
/**
|
||||
* Read a command line
|
||||
*
|
||||
@ -42,13 +41,13 @@ void shutdown_readline();
|
||||
*
|
||||
* @return the string read or empty
|
||||
*/
|
||||
std::string read_line(bool &do_quit);
|
||||
std::string read_line(bool& do_quit);
|
||||
|
||||
/**
|
||||
* Save a line to history (or do nothing when readline is not active)
|
||||
*
|
||||
* @param line a line.
|
||||
*/
|
||||
void save_line(const std::string &line);
|
||||
void save_line(const std::string& line);
|
||||
|
||||
} // namespace Mu
|
||||
|
||||
@ -17,32 +17,32 @@
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MU_RESULT_HH__
|
||||
#define MU_RESULT_HH__
|
||||
|
||||
|
||||
#include "expected.hpp"
|
||||
#include "utils/mu-error.hh"
|
||||
|
||||
|
||||
namespace Mu {
|
||||
/**
|
||||
* A Result is _either_ some value of type T, _or_ an error.
|
||||
*/
|
||||
template <typename T> using Result = tl::expected<T, Error>;
|
||||
|
||||
template <typename T> typename Result<T>::expected_type
|
||||
Ok(T&& t) {
|
||||
return Result<T>::expected(std::move(t));
|
||||
template <typename T>
|
||||
typename Result<T>::expected_type
|
||||
Ok(T&& t)
|
||||
{
|
||||
return Result<T>::expected(std::move(t));
|
||||
}
|
||||
|
||||
template <typename T> typename Result<T>::unexpected_type
|
||||
Err(Error&& err) {
|
||||
return Result<T>::unexpected(std::move(err));
|
||||
template <typename T>
|
||||
typename Result<T>::unexpected_type
|
||||
Err(Error&& err)
|
||||
{
|
||||
return Result<T>::unexpected(std::move(err));
|
||||
}
|
||||
|
||||
} // namespace Mu
|
||||
|
||||
|
||||
#endif /* MU_ERROR_HH__ */
|
||||
|
||||
@ -18,7 +18,6 @@
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#include "mu-sexp.hh"
|
||||
#include "mu-utils.hh"
|
||||
|
||||
@ -30,231 +29,221 @@ using namespace Mu;
|
||||
__attribute__((format(printf, 2, 0))) static Mu::Error
|
||||
parsing_error(size_t pos, const char* frm, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, frm);
|
||||
auto msg = vformat(frm, args);
|
||||
va_end(args);
|
||||
va_list args;
|
||||
va_start(args, frm);
|
||||
auto msg = vformat(frm, args);
|
||||
va_end(args);
|
||||
|
||||
if (pos == 0)
|
||||
return Mu::Error(Error::Code::Parsing, "%s", msg.c_str());
|
||||
else
|
||||
return Mu::Error(Error::Code::Parsing, "%zu: %s", pos, msg.c_str());
|
||||
if (pos == 0)
|
||||
return Mu::Error(Error::Code::Parsing, "%s", msg.c_str());
|
||||
else
|
||||
return Mu::Error(Error::Code::Parsing, "%zu: %s", pos, msg.c_str());
|
||||
}
|
||||
static size_t
|
||||
skip_whitespace (const std::string& s, size_t pos)
|
||||
skip_whitespace(const std::string& s, size_t pos)
|
||||
{
|
||||
while (pos != s.size()) {
|
||||
if (s[pos] == ' ' || s[pos] == '\t' || s[pos] == '\n')
|
||||
++pos;
|
||||
else
|
||||
break;
|
||||
}
|
||||
while (pos != s.size()) {
|
||||
if (s[pos] == ' ' || s[pos] == '\t' || s[pos] == '\n')
|
||||
++pos;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static Sexp parse (const std::string& expr, size_t& pos);
|
||||
static Sexp parse(const std::string& expr, size_t& pos);
|
||||
|
||||
static Sexp
|
||||
parse_list (const std::string& expr, size_t& pos)
|
||||
parse_list(const std::string& expr, size_t& pos)
|
||||
{
|
||||
if (expr[pos] != '(') // sanity check.
|
||||
throw parsing_error(pos, "expected: '(' but got '%c", expr[pos]);
|
||||
if (expr[pos] != '(') // sanity check.
|
||||
throw parsing_error(pos, "expected: '(' but got '%c", expr[pos]);
|
||||
|
||||
Sexp::List list;
|
||||
Sexp::List list;
|
||||
|
||||
++pos;
|
||||
while (expr[pos] != ')' && pos != expr.size())
|
||||
list.add(parse(expr, pos));
|
||||
++pos;
|
||||
while (expr[pos] != ')' && pos != expr.size())
|
||||
list.add(parse(expr, pos));
|
||||
|
||||
if (expr[pos] != ')')
|
||||
throw parsing_error(pos, "expected: ')' but got '%c'", expr[pos]);
|
||||
++pos;
|
||||
return Sexp::make_list(std::move(list));
|
||||
if (expr[pos] != ')')
|
||||
throw parsing_error(pos, "expected: ')' but got '%c'", expr[pos]);
|
||||
++pos;
|
||||
return Sexp::make_list(std::move(list));
|
||||
}
|
||||
|
||||
// parse string
|
||||
static Sexp
|
||||
parse_string (const std::string& expr, size_t& pos)
|
||||
parse_string(const std::string& expr, size_t& pos)
|
||||
{
|
||||
if (expr[pos] != '"') // sanity check.
|
||||
throw parsing_error(pos, "expected: '\"'' but got '%c", expr[pos]);
|
||||
if (expr[pos] != '"') // sanity check.
|
||||
throw parsing_error(pos, "expected: '\"'' but got '%c", expr[pos]);
|
||||
|
||||
bool escape{};
|
||||
std::string str;
|
||||
for (++pos; pos != expr.size(); ++pos) {
|
||||
bool escape{};
|
||||
std::string str;
|
||||
for (++pos; pos != expr.size(); ++pos) {
|
||||
auto kar = expr[pos];
|
||||
if (escape && (kar == '"' || kar == '\\')) {
|
||||
str += kar;
|
||||
escape = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto kar = expr[pos];
|
||||
if (escape && (kar == '"' || kar == '\\')) {
|
||||
str += kar;
|
||||
escape = false;
|
||||
continue;
|
||||
}
|
||||
if (kar == '"')
|
||||
break;
|
||||
else if (kar == '\\')
|
||||
escape = true;
|
||||
else
|
||||
str += kar;
|
||||
}
|
||||
|
||||
if (kar == '"')
|
||||
break;
|
||||
else if (kar == '\\')
|
||||
escape = true;
|
||||
else
|
||||
str += kar;
|
||||
}
|
||||
if (escape || expr[pos] != '"')
|
||||
throw parsing_error(pos, "unterminated string '%s'", str.c_str());
|
||||
|
||||
if (escape || expr[pos] != '"')
|
||||
throw parsing_error(pos, "unterminated string '%s'", str.c_str());
|
||||
|
||||
++pos;
|
||||
return Sexp::make_string(std::move(str));
|
||||
++pos;
|
||||
return Sexp::make_string(std::move(str));
|
||||
}
|
||||
|
||||
static Sexp
|
||||
parse_integer (const std::string& expr, size_t& pos)
|
||||
parse_integer(const std::string& expr, size_t& pos)
|
||||
{
|
||||
if (!isdigit(expr[pos]) && expr[pos] != '-') // sanity check.
|
||||
throw parsing_error(pos, "expected: <digit> but got '%c", expr[pos]);
|
||||
if (!isdigit(expr[pos]) && expr[pos] != '-') // sanity check.
|
||||
throw parsing_error(pos, "expected: <digit> but got '%c", expr[pos]);
|
||||
|
||||
std::string num; // negative number?
|
||||
if (expr[pos] == '-') {
|
||||
num = "-";
|
||||
++pos;
|
||||
}
|
||||
std::string num; // negative number?
|
||||
if (expr[pos] == '-') {
|
||||
num = "-";
|
||||
++pos;
|
||||
}
|
||||
|
||||
for (; isdigit(expr[pos]); ++pos)
|
||||
num += expr[pos];
|
||||
for (; isdigit(expr[pos]); ++pos)
|
||||
num += expr[pos];
|
||||
|
||||
return Sexp::make_number(::atoi(num.c_str()));
|
||||
return Sexp::make_number(::atoi(num.c_str()));
|
||||
}
|
||||
|
||||
static Sexp
|
||||
parse_symbol (const std::string& expr, size_t& pos)
|
||||
parse_symbol(const std::string& expr, size_t& pos)
|
||||
{
|
||||
if (!isalpha(expr[pos]) && expr[pos] != ':') // sanity check.
|
||||
throw parsing_error(pos, "expected: <alpha>|: but got '%c", expr[pos]);
|
||||
if (!isalpha(expr[pos]) && expr[pos] != ':') // sanity check.
|
||||
throw parsing_error(pos, "expected: <alpha>|: but got '%c", expr[pos]);
|
||||
|
||||
std::string symbol(1, expr[pos]);
|
||||
for (++pos; isalnum(expr[pos]) || expr[pos] == '-'; ++pos)
|
||||
symbol += expr[pos];
|
||||
std::string symbol(1, expr[pos]);
|
||||
for (++pos; isalnum(expr[pos]) || expr[pos] == '-'; ++pos)
|
||||
symbol += expr[pos];
|
||||
|
||||
return Sexp::make_symbol(std::move(symbol));
|
||||
return Sexp::make_symbol(std::move(symbol));
|
||||
}
|
||||
|
||||
|
||||
static Sexp
|
||||
parse (const std::string& expr, size_t& pos)
|
||||
parse(const std::string& expr, size_t& pos)
|
||||
{
|
||||
pos = skip_whitespace(expr, pos);
|
||||
pos = skip_whitespace(expr, pos);
|
||||
|
||||
if (pos == expr.size())
|
||||
throw parsing_error(pos, "expected: character '%c", expr[pos]);
|
||||
if (pos == expr.size())
|
||||
throw parsing_error(pos, "expected: character '%c", expr[pos]);
|
||||
|
||||
const auto kar = expr[pos];
|
||||
const auto node =[&]() -> Sexp {
|
||||
if (kar == '(')
|
||||
return parse_list (expr, pos);
|
||||
else if (kar == '"')
|
||||
return parse_string(expr, pos);
|
||||
else if (isdigit(kar) || kar == '-')
|
||||
return parse_integer(expr, pos);
|
||||
else if (isalpha(kar) || kar == ':')
|
||||
return parse_symbol(expr, pos);
|
||||
else
|
||||
throw parsing_error(pos, "unexpected character '%c", kar);
|
||||
}();
|
||||
const auto kar = expr[pos];
|
||||
const auto node = [&]() -> Sexp {
|
||||
if (kar == '(')
|
||||
return parse_list(expr, pos);
|
||||
else if (kar == '"')
|
||||
return parse_string(expr, pos);
|
||||
else if (isdigit(kar) || kar == '-')
|
||||
return parse_integer(expr, pos);
|
||||
else if (isalpha(kar) || kar == ':')
|
||||
return parse_symbol(expr, pos);
|
||||
else
|
||||
throw parsing_error(pos, "unexpected character '%c", kar);
|
||||
}();
|
||||
|
||||
pos = skip_whitespace(expr, pos);
|
||||
pos = skip_whitespace(expr, pos);
|
||||
|
||||
return node;
|
||||
return node;
|
||||
}
|
||||
|
||||
Sexp
|
||||
Sexp::make_parse (const std::string& expr)
|
||||
Sexp::make_parse(const std::string& expr)
|
||||
{
|
||||
size_t pos{};
|
||||
auto node{::parse (expr, pos)};
|
||||
size_t pos{};
|
||||
auto node{::parse(expr, pos)};
|
||||
|
||||
if (pos != expr.size())
|
||||
throw parsing_error(pos, "trailing data starting with '%c'", expr[pos]);
|
||||
if (pos != expr.size())
|
||||
throw parsing_error(pos, "trailing data starting with '%c'", expr[pos]);
|
||||
|
||||
return node;
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
Sexp::to_sexp_string () const
|
||||
Sexp::to_sexp_string() const
|
||||
{
|
||||
std::stringstream sstrm;
|
||||
std::stringstream sstrm;
|
||||
|
||||
switch (type()) {
|
||||
case Type::List: {
|
||||
sstrm << '(';
|
||||
bool first{true};
|
||||
for (auto&& child : list()) {
|
||||
sstrm << (first ? "" : " ") << child.to_sexp_string();
|
||||
first = false;
|
||||
}
|
||||
sstrm << ')';
|
||||
break;
|
||||
}
|
||||
case Type::String:
|
||||
sstrm << quote(value());
|
||||
break;
|
||||
case Type::Number:
|
||||
case Type::Symbol:
|
||||
case Type::Empty:
|
||||
default:
|
||||
sstrm << value();
|
||||
}
|
||||
switch (type()) {
|
||||
case Type::List: {
|
||||
sstrm << '(';
|
||||
bool first{true};
|
||||
for (auto&& child : list()) {
|
||||
sstrm << (first ? "" : " ") << child.to_sexp_string();
|
||||
first = false;
|
||||
}
|
||||
sstrm << ')';
|
||||
break;
|
||||
}
|
||||
case Type::String: sstrm << quote(value()); break;
|
||||
case Type::Number:
|
||||
case Type::Symbol:
|
||||
case Type::Empty:
|
||||
default: sstrm << value();
|
||||
}
|
||||
|
||||
return sstrm.str();
|
||||
return sstrm.str();
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
Sexp::to_json_string () const
|
||||
Sexp::to_json_string() const
|
||||
{
|
||||
std::stringstream sstrm;
|
||||
std::stringstream sstrm;
|
||||
|
||||
switch (type()) {
|
||||
case Type::List: {
|
||||
// property-lists become JSON objects
|
||||
if (is_prop_list()) {
|
||||
sstrm << "{";
|
||||
auto it{list().begin()};
|
||||
bool first{true};
|
||||
while (it != list().end()) {
|
||||
sstrm << (first?"":",") << quote(it->value()) << ":";
|
||||
++it;
|
||||
sstrm << it->to_json_string();
|
||||
++it;
|
||||
first = false;
|
||||
}
|
||||
sstrm << "}";
|
||||
} else { // other lists become arrays.
|
||||
sstrm << '[';
|
||||
bool first{true};
|
||||
for (auto&& child : list()) {
|
||||
sstrm << (first ? "" : ", ") << child.to_json_string();
|
||||
first = false;
|
||||
}
|
||||
sstrm << ']';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type::String:
|
||||
sstrm << quote(value());
|
||||
break;
|
||||
case Type::Symbol:
|
||||
if (is_nil())
|
||||
sstrm << "false";
|
||||
else if (is_t())
|
||||
sstrm << "true";
|
||||
else
|
||||
sstrm << quote(value());
|
||||
break;
|
||||
case Type::Number:
|
||||
case Type::Empty:
|
||||
default:
|
||||
sstrm << value();
|
||||
}
|
||||
switch (type()) {
|
||||
case Type::List: {
|
||||
// property-lists become JSON objects
|
||||
if (is_prop_list()) {
|
||||
sstrm << "{";
|
||||
auto it{list().begin()};
|
||||
bool first{true};
|
||||
while (it != list().end()) {
|
||||
sstrm << (first ? "" : ",") << quote(it->value()) << ":";
|
||||
++it;
|
||||
sstrm << it->to_json_string();
|
||||
++it;
|
||||
first = false;
|
||||
}
|
||||
sstrm << "}";
|
||||
} else { // other lists become arrays.
|
||||
sstrm << '[';
|
||||
bool first{true};
|
||||
for (auto&& child : list()) {
|
||||
sstrm << (first ? "" : ", ") << child.to_json_string();
|
||||
first = false;
|
||||
}
|
||||
sstrm << ']';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type::String: sstrm << quote(value()); break;
|
||||
case Type::Symbol:
|
||||
if (is_nil())
|
||||
sstrm << "false";
|
||||
else if (is_t())
|
||||
sstrm << "true";
|
||||
else
|
||||
sstrm << quote(value());
|
||||
break;
|
||||
case Type::Number:
|
||||
case Type::Empty:
|
||||
default: sstrm << value();
|
||||
}
|
||||
|
||||
return sstrm.str();
|
||||
return sstrm.str();
|
||||
}
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MU_SEXP_HH__
|
||||
#define MU_SEXP_HH__
|
||||
|
||||
@ -35,331 +34,344 @@ namespace Mu {
|
||||
///
|
||||
/// (:foo (1234 "bar" nil) :quux (a b c))
|
||||
|
||||
|
||||
/// Parse node
|
||||
struct Sexp {
|
||||
/// Node type
|
||||
enum struct Type { Empty, List, String, Number, Symbol };
|
||||
/// Node type
|
||||
enum struct Type { Empty, List, String, Number, Symbol };
|
||||
|
||||
/**
|
||||
* Default CTOR
|
||||
*/
|
||||
Sexp() : type_{Type::Empty} {}
|
||||
|
||||
/**
|
||||
* Default CTOR
|
||||
*/
|
||||
Sexp():type_{Type::Empty}{}
|
||||
// Underlying data type for list; we'd like to use std::dequeu here,
|
||||
// but that does not compile with libc++ (it does with libstdc++)
|
||||
using Seq = std::vector<Sexp>;
|
||||
|
||||
// Underlying data type for list; we'd like to use std::dequeu here,
|
||||
// but that does not compile with libc++ (it does with libstdc++)
|
||||
using Seq = std::vector<Sexp>;
|
||||
/**
|
||||
* Make a sexp out of an s-expression string.
|
||||
*
|
||||
* @param expr a string containing an s-expression
|
||||
*
|
||||
* @return the parsed s-expression, or throw Error.
|
||||
*/
|
||||
static Sexp make_parse(const std::string& expr);
|
||||
|
||||
/**
|
||||
* Make a sexp out of an s-expression string.
|
||||
*
|
||||
* @param expr a string containing an s-expression
|
||||
*
|
||||
* @return the parsed s-expression, or throw Error.
|
||||
*/
|
||||
static Sexp make_parse (const std::string& expr);
|
||||
/**
|
||||
* Make a node for a string/integer/symbol/list value
|
||||
*
|
||||
* @param val some value
|
||||
*
|
||||
* @return a node
|
||||
*/
|
||||
static Sexp make_string(std::string&& val) { return Sexp{Type::String, std::move(val)}; }
|
||||
static Sexp make_string(const std::string& val)
|
||||
{
|
||||
return Sexp{Type::String, std::string(val)};
|
||||
}
|
||||
static Sexp make_number(int val) { return Sexp{Type::Number, format("%d", val)}; }
|
||||
static Sexp make_symbol(std::string&& val)
|
||||
{
|
||||
if (val.empty())
|
||||
throw Error(Error::Code::InvalidArgument, "symbol must be non-empty");
|
||||
return Sexp{Type::Symbol, std::move(val)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a node for a string/integer/symbol/list value
|
||||
*
|
||||
* @param val some value
|
||||
*
|
||||
* @return a node
|
||||
*/
|
||||
static Sexp make_string (std::string&& val) { return Sexp{Type::String, std::move(val)}; }
|
||||
static Sexp make_string (const std::string& val) { return Sexp{Type::String, std::string(val)}; }
|
||||
static Sexp make_number (int val) { return Sexp{Type::Number, format("%d", val)}; }
|
||||
static Sexp make_symbol (std::string&& val) {
|
||||
if (val.empty())
|
||||
throw Error(Error::Code::InvalidArgument, "symbol must be non-empty");
|
||||
return Sexp{Type::Symbol, std::move(val)};
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* The value of this node; invalid for list nodes.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
const std::string& value() const
|
||||
{
|
||||
if (is_list())
|
||||
throw Error(Error::Code::InvalidArgument, "no value for list");
|
||||
if (is_empty())
|
||||
throw Error{Error::Code::InvalidArgument, "no value for empty"};
|
||||
return value_;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* The value of this node; invalid for list nodes.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
const std::string& value() const {
|
||||
if (is_list())
|
||||
throw Error(Error::Code::InvalidArgument, "no value for list");
|
||||
if (is_empty())
|
||||
throw Error{Error::Code::InvalidArgument, "no value for empty"};
|
||||
return value_;
|
||||
}
|
||||
/**
|
||||
* The underlying container of this list node; only valid for lists
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
const Seq& list() const
|
||||
{
|
||||
if (!is_list())
|
||||
throw Error(Error::Code::InvalidArgument, "not a list");
|
||||
return seq_;
|
||||
}
|
||||
|
||||
/**
|
||||
* The underlying container of this list node; only valid for lists
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
const Seq& list() const {
|
||||
if (!is_list())
|
||||
throw Error(Error::Code::InvalidArgument, "not a list");
|
||||
return seq_;
|
||||
}
|
||||
/**
|
||||
* Convert a Sexp::Node to its S-expression string representation
|
||||
*
|
||||
* @return the string representation
|
||||
*/
|
||||
std::string to_sexp_string() const;
|
||||
|
||||
/**
|
||||
* Convert a Sexp::Node to its S-expression string representation
|
||||
*
|
||||
* @return the string representation
|
||||
*/
|
||||
std::string to_sexp_string() const;
|
||||
/**
|
||||
* Convert a Sexp::Node to its JSON string representation
|
||||
*
|
||||
* @return the string representation
|
||||
*/
|
||||
std::string to_json_string() const;
|
||||
|
||||
/**
|
||||
* Return the type of this Node.
|
||||
*
|
||||
* @return the type
|
||||
*/
|
||||
Type type() const { return type_; }
|
||||
|
||||
/**
|
||||
* Convert a Sexp::Node to its JSON string representation
|
||||
*
|
||||
* @return the string representation
|
||||
*/
|
||||
std::string to_json_string() const;
|
||||
///
|
||||
/// Helper struct to build mutable lists.
|
||||
///
|
||||
struct List {
|
||||
/**
|
||||
* Add a sexp to the list
|
||||
*
|
||||
* @param sexp a sexp
|
||||
* @param args rest arguments
|
||||
*
|
||||
* @return a ref to this List (for chaining)
|
||||
*/
|
||||
List& add() { return *this; }
|
||||
List& add(Sexp&& sexp)
|
||||
{
|
||||
seq_.emplace_back(std::move(sexp));
|
||||
return *this;
|
||||
}
|
||||
template <typename... Args> List& add(Sexp&& sexp, Args... args)
|
||||
{
|
||||
seq_.emplace_back(std::move(sexp));
|
||||
seq_.emplace_back(std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of this Node.
|
||||
*
|
||||
* @return the type
|
||||
*/
|
||||
Type type() const { return type_; }
|
||||
/**
|
||||
* Add a property (i.e., :key sexp ) to the list
|
||||
*
|
||||
* @param name a property-name. Must start with ':', length > 1
|
||||
* @param sexp a sexp
|
||||
* @param args rest arguments
|
||||
*
|
||||
* @return a ref to this List (for chaining)
|
||||
*/
|
||||
List& add_prop(std::string&& name, Sexp&& sexp)
|
||||
{
|
||||
if (!is_prop_name(name))
|
||||
throw Error{Error::Code::InvalidArgument,
|
||||
"invalid property name ('%s')",
|
||||
name.c_str()};
|
||||
seq_.emplace_back(make_symbol(std::move(name)));
|
||||
seq_.emplace_back(std::move(sexp));
|
||||
return *this;
|
||||
}
|
||||
template <typename... Args>
|
||||
List& add_prop(std::string&& name, Sexp&& sexp, Args... args)
|
||||
{
|
||||
add_prop(std::move(name), std::move(sexp));
|
||||
add_prop(std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// Helper struct to build mutable lists.
|
||||
///
|
||||
struct List {
|
||||
/**
|
||||
* Add a sexp to the list
|
||||
*
|
||||
* @param sexp a sexp
|
||||
* @param args rest arguments
|
||||
*
|
||||
* @return a ref to this List (for chaining)
|
||||
*/
|
||||
List& add () { return *this; }
|
||||
List& add (Sexp&& sexp) { seq_.emplace_back(std::move(sexp)); return *this; }
|
||||
template <typename... Args> List& add (Sexp&& sexp, Args... args) {
|
||||
seq_.emplace_back(std::move(sexp));
|
||||
seq_.emplace_back(std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
/**
|
||||
* Get the number of elements in the list
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
size_t size() const { return seq_.size(); }
|
||||
|
||||
/**
|
||||
* Add a property (i.e., :key sexp ) to the list
|
||||
*
|
||||
* @param name a property-name. Must start with ':', length > 1
|
||||
* @param sexp a sexp
|
||||
* @param args rest arguments
|
||||
*
|
||||
* @return a ref to this List (for chaining)
|
||||
*/
|
||||
List& add_prop (std::string&& name, Sexp&& sexp) {
|
||||
if (!is_prop_name(name))
|
||||
throw Error{Error::Code::InvalidArgument, "invalid property name ('%s')",
|
||||
name.c_str()};
|
||||
seq_.emplace_back(make_symbol(std::move(name)));
|
||||
seq_.emplace_back(std::move(sexp));
|
||||
return *this;
|
||||
}
|
||||
template <typename... Args>
|
||||
List& add_prop (std::string&& name, Sexp&& sexp, Args... args) {
|
||||
add_prop(std::move(name), std::move(sexp));
|
||||
add_prop(std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
/**
|
||||
* Is the list empty?
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
size_t empty() const { return seq_.empty(); }
|
||||
|
||||
/**
|
||||
* Get the number of elements in the list
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
size_t size() const { return seq_.size(); }
|
||||
private:
|
||||
friend struct Sexp;
|
||||
Seq seq_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Is the list empty?
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
size_t empty() const { return seq_.empty(); }
|
||||
/**
|
||||
* Construct a list sexp from a List
|
||||
*
|
||||
* @param list a list-list
|
||||
* @param sexp a Sexp
|
||||
* @param args rest arguments
|
||||
*
|
||||
* @return a sexp.
|
||||
*/
|
||||
static Sexp make_list(List&& list) { return Sexp{Type::List, std::move(list.seq_)}; }
|
||||
template <typename... Args> static Sexp make_list(Sexp&& sexp, Args... args)
|
||||
{
|
||||
List lst;
|
||||
lst.add(std::move(sexp)).add(std::forward<Args>(args)...);
|
||||
return make_list(std::move(lst));
|
||||
}
|
||||
|
||||
private:
|
||||
friend struct Sexp;
|
||||
Seq seq_;
|
||||
};
|
||||
/**
|
||||
* Construct a property list sexp from a List
|
||||
*
|
||||
* @param name the property name; must start wtth ':'
|
||||
* @param sexp a Sexp
|
||||
* @param args rest arguments (property list)
|
||||
*
|
||||
* @return a sexp.
|
||||
*/
|
||||
template <typename... Args>
|
||||
static Sexp make_prop_list(std::string&& name, Sexp&& sexp, Args... args)
|
||||
{
|
||||
List list;
|
||||
list.add_prop(std::move(name), std::move(sexp), std::forward<Args>(args)...);
|
||||
return make_list(std::move(list));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a list sexp from a List
|
||||
*
|
||||
* @param list a list-list
|
||||
* @param sexp a Sexp
|
||||
* @param args rest arguments
|
||||
*
|
||||
* @return a sexp.
|
||||
*/
|
||||
static Sexp make_list(List&& list) {
|
||||
return Sexp{Type::List, std::move(list.seq_)};
|
||||
}
|
||||
template <typename ... Args>
|
||||
static Sexp make_list(Sexp&& sexp, Args... args) {
|
||||
List lst;
|
||||
lst.add(std::move(sexp)).add(std::forward<Args>(args)...);
|
||||
return make_list(std::move(lst));
|
||||
}
|
||||
/**
|
||||
* Construct a properrty list sexp from a List
|
||||
*
|
||||
* @param funcname function name for the call
|
||||
* @param name the property name; must start wtth ':'
|
||||
* @param sexp a Sexp
|
||||
* @param args rest arguments (property list)
|
||||
*
|
||||
* @return a sexp.
|
||||
*/
|
||||
template <typename... Args>
|
||||
static Sexp make_call(std::string&& funcname, std::string&& name, Sexp&& sexp, Args... args)
|
||||
{
|
||||
List list;
|
||||
list.add(make_symbol(std::move(funcname)));
|
||||
list.add_prop(std::move(name), std::move(sexp), std::forward<Args>(args)...);
|
||||
return make_list(std::move(list));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a property list sexp from a List
|
||||
*
|
||||
* @param name the property name; must start wtth ':'
|
||||
* @param sexp a Sexp
|
||||
* @param args rest arguments (property list)
|
||||
*
|
||||
* @return a sexp.
|
||||
*/
|
||||
template <typename ... Args>
|
||||
static Sexp make_prop_list(std::string&& name, Sexp&& sexp, Args... args) {
|
||||
List list;
|
||||
list.add_prop(std::move(name), std::move(sexp), std::forward<Args>(args)...);
|
||||
return make_list(std::move(list));
|
||||
/// Some type helpers
|
||||
bool is_list() const { return type() == Type::List; }
|
||||
bool is_string() const { return type() == Type::String; }
|
||||
bool is_number() const { return type() == Type::Number; }
|
||||
bool is_symbol() const { return type() == Type::Symbol; }
|
||||
bool is_empty() const { return type() == Type::Empty; }
|
||||
|
||||
}
|
||||
operator bool() const { return !is_empty(); }
|
||||
|
||||
/**
|
||||
* Construct a properrty list sexp from a List
|
||||
*
|
||||
* @param funcname function name for the call
|
||||
* @param name the property name; must start wtth ':'
|
||||
* @param sexp a Sexp
|
||||
* @param args rest arguments (property list)
|
||||
*
|
||||
* @return a sexp.
|
||||
*/
|
||||
template <typename ... Args>
|
||||
static Sexp make_call(std::string&& funcname, std::string&& name, Sexp&& sexp, Args... args) {
|
||||
List list;
|
||||
list.add(make_symbol(std::move(funcname)));
|
||||
list.add_prop(std::move(name), std::move(sexp), std::forward<Args>(args)...);
|
||||
return make_list(std::move(list));
|
||||
static constexpr auto SymbolNil{"nil"};
|
||||
static constexpr auto SymbolT{"t"};
|
||||
bool is_nil() const { return is_symbol() && value() == SymbolNil; }
|
||||
bool is_t() const { return is_symbol() && value() == SymbolT; }
|
||||
|
||||
}
|
||||
/**
|
||||
* Is this a prop-list? A prop list is a list sexp with alternating
|
||||
* property / sexp
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
bool is_prop_list() const
|
||||
{
|
||||
if (!is_list() || list().size() % 2 != 0)
|
||||
return false;
|
||||
else
|
||||
return is_prop_list(list().begin(), list().end());
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a call? A call is a list sexp with a symbol (function name),
|
||||
* followed by a prop list
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
bool is_call() const
|
||||
{
|
||||
if (!is_list() || list().size() % 2 != 1 || !list().at(0).is_symbol())
|
||||
return false;
|
||||
else
|
||||
return is_prop_list(list().begin() + 1, list().end());
|
||||
};
|
||||
|
||||
/// Some type helpers
|
||||
bool is_list() const { return type() == Type::List; }
|
||||
bool is_string() const { return type() == Type::String; }
|
||||
bool is_number() const { return type() == Type::Number; }
|
||||
bool is_symbol() const { return type() == Type::Symbol; }
|
||||
bool is_empty() const { return type() == Type::Empty; }
|
||||
private:
|
||||
Sexp(Type typearg, std::string&& valuearg) : type_{typearg}, value_{std::move(valuearg)}
|
||||
{
|
||||
if (is_list())
|
||||
throw Error{Error::Code::InvalidArgument, "cannot be a list type"};
|
||||
if (is_empty())
|
||||
throw Error{Error::Code::InvalidArgument, "cannot be an empty type"};
|
||||
}
|
||||
Sexp(Type typearg, Seq&& seq) : type_{Type::List}, seq_{std::move(seq)}
|
||||
{
|
||||
if (!is_list())
|
||||
throw Error{Error::Code::InvalidArgument, "must be a list type"};
|
||||
if (is_empty())
|
||||
throw Error{Error::Code::InvalidArgument, "cannot be an empty type"};
|
||||
}
|
||||
/**
|
||||
* Is the sexp a valid property name?
|
||||
*
|
||||
* @param sexp a Sexp.
|
||||
*
|
||||
* @return true or false.
|
||||
*/
|
||||
static bool is_prop_name(const std::string& str)
|
||||
{
|
||||
return str.size() > 1 && str.at(0) == ':';
|
||||
}
|
||||
static bool is_prop_name(const Sexp& sexp)
|
||||
{
|
||||
return sexp.is_symbol() && is_prop_name(sexp.value());
|
||||
}
|
||||
|
||||
operator bool() const { return !is_empty(); }
|
||||
static bool is_prop_list(Seq::const_iterator b, Seq::const_iterator e)
|
||||
{
|
||||
while (b != e) {
|
||||
const Sexp& s{*b};
|
||||
if (!is_prop_name(s))
|
||||
return false;
|
||||
if (++b == e)
|
||||
return false;
|
||||
++b;
|
||||
}
|
||||
return b == e;
|
||||
}
|
||||
|
||||
static constexpr auto SymbolNil{"nil"};
|
||||
static constexpr auto SymbolT{"t"};
|
||||
bool is_nil() const { return is_symbol() && value() == SymbolNil; }
|
||||
bool is_t() const { return is_symbol() && value() == SymbolT; }
|
||||
|
||||
/**
|
||||
* Is this a prop-list? A prop list is a list sexp with alternating
|
||||
* property / sexp
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
bool is_prop_list() const {
|
||||
if (!is_list() || list().size() % 2 != 0)
|
||||
return false;
|
||||
else
|
||||
return is_prop_list(list().begin(), list().end());
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a call? A call is a list sexp with a symbol (function name),
|
||||
* followed by a prop list
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
bool is_call() const {
|
||||
if (!is_list() || list().size() % 2 != 1 || !list().at(0).is_symbol())
|
||||
return false;
|
||||
else
|
||||
return is_prop_list (list().begin()+1, list().end());
|
||||
};
|
||||
|
||||
private:
|
||||
Sexp(Type typearg, std::string&& valuearg): type_{typearg}, value_{std::move(valuearg)} {
|
||||
if (is_list())
|
||||
throw Error{Error::Code::InvalidArgument, "cannot be a list type"};
|
||||
if (is_empty())
|
||||
throw Error{Error::Code::InvalidArgument, "cannot be an empty type"};
|
||||
|
||||
}
|
||||
Sexp(Type typearg, Seq&& seq): type_{Type::List}, seq_{std::move(seq)}{
|
||||
if (!is_list())
|
||||
throw Error{Error::Code::InvalidArgument, "must be a list type"};
|
||||
if (is_empty())
|
||||
throw Error{Error::Code::InvalidArgument, "cannot be an empty type"};
|
||||
}
|
||||
/**
|
||||
* Is the sexp a valid property name?
|
||||
*
|
||||
* @param sexp a Sexp.
|
||||
*
|
||||
* @return true or false.
|
||||
*/
|
||||
static bool is_prop_name(const std::string& str) {
|
||||
return str.size() > 1 && str.at(0) == ':';
|
||||
}
|
||||
static bool is_prop_name(const Sexp& sexp) {
|
||||
return sexp.is_symbol() && is_prop_name(sexp.value());
|
||||
}
|
||||
|
||||
static bool is_prop_list (Seq::const_iterator b, Seq::const_iterator e) {
|
||||
while (b != e) {
|
||||
const Sexp& s{*b};
|
||||
if (!is_prop_name(s))
|
||||
return false;
|
||||
if (++b == e)
|
||||
return false;
|
||||
++b;
|
||||
}
|
||||
return b == e;
|
||||
}
|
||||
|
||||
const Type type_; /**< Type of node */
|
||||
const std::string value_; /**< String value of node (only for
|
||||
* non-Type::Lst)*/
|
||||
const Seq seq_; /**< Children of node (only for
|
||||
* Type::Lst) */
|
||||
const Type type_; /**< Type of node */
|
||||
const std::string value_; /**< String value of node (only for
|
||||
* non-Type::Lst)*/
|
||||
const Seq seq_; /**< Children of node (only for
|
||||
* Type::Lst) */
|
||||
};
|
||||
|
||||
static inline std::ostream&
|
||||
operator<<(std::ostream& os, Sexp::Type id)
|
||||
{
|
||||
switch (id) {
|
||||
case Sexp::Type::List: os << "list"; break;
|
||||
case Sexp::Type::String: os << "string"; break;
|
||||
case Sexp::Type::Number: os << "number"; break;
|
||||
case Sexp::Type::Symbol: os << "symbol"; break;
|
||||
case Sexp::Type::Empty: os << "empty"; break;
|
||||
default: throw std::runtime_error ("unknown node type");
|
||||
}
|
||||
switch (id) {
|
||||
case Sexp::Type::List: os << "list"; break;
|
||||
case Sexp::Type::String: os << "string"; break;
|
||||
case Sexp::Type::Number: os << "number"; break;
|
||||
case Sexp::Type::Symbol: os << "symbol"; break;
|
||||
case Sexp::Type::Empty: os << "empty"; break;
|
||||
default: throw std::runtime_error("unknown node type");
|
||||
}
|
||||
|
||||
return os;
|
||||
return os;
|
||||
}
|
||||
|
||||
static inline std::ostream&
|
||||
operator<<(std::ostream& os, const Sexp& sexp)
|
||||
{
|
||||
os << sexp.to_sexp_string();
|
||||
return os;
|
||||
os << sexp.to_sexp_string();
|
||||
return os;
|
||||
}
|
||||
|
||||
static inline std::ostream&
|
||||
operator<<(std::ostream& os, const Sexp::List& sexp)
|
||||
{
|
||||
os << Sexp::make_list(Sexp::List(sexp));
|
||||
return os;
|
||||
os << Sexp::make_list(Sexp::List(sexp));
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
} // Mu
|
||||
} // namespace Mu
|
||||
|
||||
#endif /* MU_SEXP_HH__ */
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
** 02110-1301, USA.
|
||||
*/
|
||||
|
||||
|
||||
#define _XOPEN_SOURCE
|
||||
#include <time.h>
|
||||
|
||||
@ -40,24 +39,24 @@ using namespace Mu;
|
||||
namespace {
|
||||
|
||||
static gunichar
|
||||
unichar_tolower (gunichar uc)
|
||||
unichar_tolower(gunichar uc)
|
||||
{
|
||||
if (!g_unichar_isalpha(uc))
|
||||
return uc;
|
||||
if (!g_unichar_isalpha(uc))
|
||||
return uc;
|
||||
|
||||
if (g_unichar_get_script (uc) != G_UNICODE_SCRIPT_LATIN)
|
||||
return g_unichar_tolower (uc);
|
||||
if (g_unichar_get_script(uc) != G_UNICODE_SCRIPT_LATIN)
|
||||
return g_unichar_tolower(uc);
|
||||
|
||||
switch (uc)
|
||||
{
|
||||
case 0x00e6:
|
||||
case 0x00c6: return 'e'; /* æ */
|
||||
case 0x00f8: return 'o'; /* ø */
|
||||
case 0x0110:
|
||||
case 0x0111: return 'd'; /* đ */
|
||||
/* todo: many more */
|
||||
default: return g_unichar_tolower (uc);
|
||||
}
|
||||
switch (uc) {
|
||||
case 0x00e6:
|
||||
case 0x00c6: return 'e'; /* æ */
|
||||
case 0x00f8: return 'o'; /* ø */
|
||||
case 0x0110:
|
||||
case 0x0111:
|
||||
return 'd'; /* đ */
|
||||
/* todo: many more */
|
||||
default: return g_unichar_tolower(uc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,338 +69,328 @@ unichar_tolower (gunichar uc)
|
||||
* Returns: (transfer full): a flattened string, free with g_free().
|
||||
*/
|
||||
static char*
|
||||
gx_utf8_flatten (const gchar *str, gssize len)
|
||||
gx_utf8_flatten(const gchar* str, gssize len)
|
||||
{
|
||||
GString *gstr;
|
||||
char *norm, *cur;
|
||||
GString* gstr;
|
||||
char * norm, *cur;
|
||||
|
||||
g_return_val_if_fail (str, NULL);
|
||||
g_return_val_if_fail(str, NULL);
|
||||
|
||||
norm = g_utf8_normalize (str, len, G_NORMALIZE_ALL);
|
||||
if (!norm)
|
||||
return NULL;
|
||||
norm = g_utf8_normalize(str, len, G_NORMALIZE_ALL);
|
||||
if (!norm)
|
||||
return NULL;
|
||||
|
||||
gstr = g_string_sized_new (strlen (norm));
|
||||
gstr = g_string_sized_new(strlen(norm));
|
||||
|
||||
for (cur = norm; cur && *cur; cur = g_utf8_next_char (cur))
|
||||
{
|
||||
gunichar uc;
|
||||
for (cur = norm; cur && *cur; cur = g_utf8_next_char(cur)) {
|
||||
gunichar uc;
|
||||
|
||||
uc = g_utf8_get_char (cur);
|
||||
if (g_unichar_combining_class (uc) != 0)
|
||||
continue;
|
||||
uc = g_utf8_get_char(cur);
|
||||
if (g_unichar_combining_class(uc) != 0)
|
||||
continue;
|
||||
|
||||
g_string_append_unichar (gstr, unichar_tolower(uc));
|
||||
}
|
||||
g_string_append_unichar(gstr, unichar_tolower(uc));
|
||||
}
|
||||
|
||||
g_free (norm);
|
||||
g_free(norm);
|
||||
|
||||
return g_string_free (gstr, FALSE);
|
||||
return g_string_free(gstr, FALSE);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string // gx_utf8_flatten
|
||||
Mu::utf8_flatten (const char *str)
|
||||
Mu::utf8_flatten(const char* str)
|
||||
{
|
||||
if (!str)
|
||||
return {};
|
||||
if (!str)
|
||||
return {};
|
||||
|
||||
// the pure-ascii case
|
||||
if (g_str_is_ascii(str)) {
|
||||
auto l = g_ascii_strdown (str, -1);
|
||||
std::string s{l};
|
||||
g_free (l);
|
||||
return s;
|
||||
}
|
||||
// the pure-ascii case
|
||||
if (g_str_is_ascii(str)) {
|
||||
auto l = g_ascii_strdown(str, -1);
|
||||
std::string s{l};
|
||||
g_free(l);
|
||||
return s;
|
||||
}
|
||||
|
||||
// seems we need the big guns
|
||||
char *flat = gx_utf8_flatten (str, -1);
|
||||
if (!flat)
|
||||
return {};
|
||||
// seems we need the big guns
|
||||
char* flat = gx_utf8_flatten(str, -1);
|
||||
if (!flat)
|
||||
return {};
|
||||
|
||||
std::string s{flat};
|
||||
g_free (flat);
|
||||
std::string s{flat};
|
||||
g_free(flat);
|
||||
|
||||
return s;
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string
|
||||
Mu::utf8_clean (const std::string& dirty)
|
||||
Mu::utf8_clean(const std::string& dirty)
|
||||
{
|
||||
GString *gstr = g_string_sized_new (dirty.length());
|
||||
GString* gstr = g_string_sized_new(dirty.length());
|
||||
|
||||
for (auto cur = dirty.c_str(); cur && *cur; cur = g_utf8_next_char (cur)) {
|
||||
for (auto cur = dirty.c_str(); cur && *cur; cur = g_utf8_next_char(cur)) {
|
||||
const gunichar uc = g_utf8_get_char(cur);
|
||||
if (g_unichar_iscntrl(uc))
|
||||
g_string_append_c(gstr, ' ');
|
||||
else
|
||||
g_string_append_unichar(gstr, uc);
|
||||
}
|
||||
|
||||
const gunichar uc = g_utf8_get_char (cur);
|
||||
if (g_unichar_iscntrl (uc))
|
||||
g_string_append_c (gstr, ' ');
|
||||
else
|
||||
g_string_append_unichar (gstr, uc);
|
||||
}
|
||||
std::string clean(gstr->str, gstr->len);
|
||||
g_string_free(gstr, TRUE);
|
||||
|
||||
std::string clean(gstr->str, gstr->len);
|
||||
g_string_free (gstr, TRUE);
|
||||
clean.erase(0, clean.find_first_not_of(" "));
|
||||
clean.erase(clean.find_last_not_of(" ") + 1); // remove trailing space
|
||||
|
||||
clean.erase (0, clean.find_first_not_of(" "));
|
||||
clean.erase (clean.find_last_not_of(" ") + 1); // remove trailing space
|
||||
|
||||
return clean;
|
||||
return clean;
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
Mu::remove_ctrl (const std::string& str)
|
||||
Mu::remove_ctrl(const std::string& str)
|
||||
{
|
||||
char prev{'\0'};
|
||||
std::string result;
|
||||
result.reserve(str.length());
|
||||
char prev{'\0'};
|
||||
std::string result;
|
||||
result.reserve(str.length());
|
||||
|
||||
for (auto&& c: str) {
|
||||
if (::iscntrl(c) || c == ' ') {
|
||||
if (prev != ' ')
|
||||
result += prev = ' ';
|
||||
} else
|
||||
result += prev = c;
|
||||
}
|
||||
for (auto&& c : str) {
|
||||
if (::iscntrl(c) || c == ' ') {
|
||||
if (prev != ' ')
|
||||
result += prev = ' ';
|
||||
} else
|
||||
result += prev = c;
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string>
|
||||
Mu::split (const std::string& str, const std::string& sepa)
|
||||
Mu::split(const std::string& str, const std::string& sepa)
|
||||
{
|
||||
char **parts = g_strsplit(str.c_str(), sepa.c_str(), -1);
|
||||
std::vector<std::string> vec;
|
||||
for (auto part = parts; part && *part; ++part)
|
||||
vec.push_back (*part);
|
||||
char** parts = g_strsplit(str.c_str(), sepa.c_str(), -1);
|
||||
std::vector<std::string> vec;
|
||||
for (auto part = parts; part && *part; ++part)
|
||||
vec.push_back(*part);
|
||||
|
||||
g_strfreev(parts);
|
||||
g_strfreev(parts);
|
||||
|
||||
return vec;
|
||||
return vec;
|
||||
}
|
||||
|
||||
std::string
|
||||
Mu::quote (const std::string& str)
|
||||
Mu::quote(const std::string& str)
|
||||
{
|
||||
std::string res{"\""};
|
||||
std::string res{"\""};
|
||||
|
||||
for (auto&& k: str) {
|
||||
switch (k) {
|
||||
case '"' : res += "\\\""; break;
|
||||
case '\\': res += "\\\\"; break;
|
||||
default: res += k;
|
||||
}
|
||||
}
|
||||
for (auto&& k : str) {
|
||||
switch (k) {
|
||||
case '"': res += "\\\""; break;
|
||||
case '\\': res += "\\\\"; break;
|
||||
default: res += k;
|
||||
}
|
||||
}
|
||||
|
||||
return res + "\"";
|
||||
return res + "\"";
|
||||
}
|
||||
|
||||
std::string
|
||||
Mu::format (const char *frm, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start (args, frm);
|
||||
auto str = vformat(frm, args);
|
||||
va_end (args);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string
|
||||
Mu::vformat (const char *frm, va_list args)
|
||||
Mu::format(const char* frm, ...)
|
||||
{
|
||||
char *s{};
|
||||
const auto res = g_vasprintf (&s, frm, args);
|
||||
if (res == -1) {
|
||||
std::cerr << "string format failed" << std::endl;
|
||||
return {};
|
||||
}
|
||||
va_list args;
|
||||
|
||||
std::string str{s};
|
||||
g_free (s);
|
||||
va_start(args, frm);
|
||||
auto str = vformat(frm, args);
|
||||
va_end(args);
|
||||
|
||||
return str;
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string
|
||||
Mu::vformat(const char* frm, va_list args)
|
||||
{
|
||||
char* s{};
|
||||
const auto res = g_vasprintf(&s, frm, args);
|
||||
if (res == -1) {
|
||||
std::cerr << "string format failed" << std::endl;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string str{s};
|
||||
g_free(s);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
constexpr const auto InternalDateFormat = "%010" G_GINT64_FORMAT;
|
||||
constexpr const char InternalDateMin[] = "0000000000";
|
||||
constexpr const char InternalDateMax[] = "9999999999";
|
||||
constexpr const char InternalDateMin[] = "0000000000";
|
||||
constexpr const char InternalDateMax[] = "9999999999";
|
||||
static_assert(sizeof(InternalDateMin) == 10 + 1, "invalid");
|
||||
static_assert(sizeof(InternalDateMax) == 10 + 1, "invalid");
|
||||
|
||||
static std::string
|
||||
date_boundary (bool is_first)
|
||||
date_boundary(bool is_first)
|
||||
{
|
||||
return is_first ? InternalDateMin : InternalDateMax;
|
||||
return is_first ? InternalDateMin : InternalDateMax;
|
||||
}
|
||||
|
||||
std::string
|
||||
Mu::date_to_time_t_string (int64_t t)
|
||||
Mu::date_to_time_t_string(int64_t t)
|
||||
{
|
||||
char buf[sizeof(InternalDateMax)];
|
||||
g_snprintf (buf, sizeof(buf), InternalDateFormat, t);
|
||||
char buf[sizeof(InternalDateMax)];
|
||||
g_snprintf(buf, sizeof(buf), InternalDateFormat, t);
|
||||
|
||||
return buf;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static std::string
|
||||
delta_ymwdhMs (const std::string& expr)
|
||||
delta_ymwdhMs(const std::string& expr)
|
||||
{
|
||||
char *endptr;
|
||||
auto num = strtol (expr.c_str(), &endptr, 10);
|
||||
if (num <= 0 || num > 9999 || !endptr || !*endptr)
|
||||
return date_boundary (true);
|
||||
char* endptr;
|
||||
auto num = strtol(expr.c_str(), &endptr, 10);
|
||||
if (num <= 0 || num > 9999 || !endptr || !*endptr)
|
||||
return date_boundary(true);
|
||||
|
||||
int years, months, weeks, days, hours, minutes, seconds;
|
||||
years = months = weeks = days = hours = minutes = seconds = 0;
|
||||
int years, months, weeks, days, hours, minutes, seconds;
|
||||
years = months = weeks = days = hours = minutes = seconds = 0;
|
||||
|
||||
switch (endptr[0]) {
|
||||
case 's': seconds = num; break;
|
||||
case 'M': minutes = num; break;
|
||||
case 'h': hours = num; break;
|
||||
case 'd': days = num; break;
|
||||
case 'w': weeks = num; break;
|
||||
case 'm': months = num; break;
|
||||
case 'y': years = num; break;
|
||||
default:
|
||||
return date_boundary (true);
|
||||
}
|
||||
switch (endptr[0]) {
|
||||
case 's': seconds = num; break;
|
||||
case 'M': minutes = num; break;
|
||||
case 'h': hours = num; break;
|
||||
case 'd': days = num; break;
|
||||
case 'w': weeks = num; break;
|
||||
case 'm': months = num; break;
|
||||
case 'y': years = num; break;
|
||||
default: return date_boundary(true);
|
||||
}
|
||||
|
||||
GDateTime *then, *now = g_date_time_new_now_local ();
|
||||
if (weeks != 0)
|
||||
then = g_date_time_add_weeks (now, -weeks);
|
||||
else
|
||||
then = g_date_time_add_full (now, -years, -months,-days,
|
||||
-hours, -minutes, -seconds);
|
||||
GDateTime *then, *now = g_date_time_new_now_local();
|
||||
if (weeks != 0)
|
||||
then = g_date_time_add_weeks(now, -weeks);
|
||||
else
|
||||
then =
|
||||
g_date_time_add_full(now, -years, -months, -days, -hours, -minutes, -seconds);
|
||||
|
||||
time_t t = MAX (0, (gint64)g_date_time_to_unix (then));
|
||||
time_t t = MAX(0, (gint64)g_date_time_to_unix(then));
|
||||
|
||||
g_date_time_unref (then);
|
||||
g_date_time_unref (now);
|
||||
g_date_time_unref(then);
|
||||
g_date_time_unref(now);
|
||||
|
||||
return date_to_time_t_string (t);
|
||||
return date_to_time_t_string(t);
|
||||
}
|
||||
|
||||
static std::string
|
||||
special_date (const std::string& d, bool is_first)
|
||||
special_date(const std::string& d, bool is_first)
|
||||
{
|
||||
if (d == "now")
|
||||
return date_to_time_t_string (time(NULL));
|
||||
if (d == "now")
|
||||
return date_to_time_t_string(time(NULL));
|
||||
|
||||
else if (d == "today") {
|
||||
else if (d == "today") {
|
||||
GDateTime *dt, *midnight;
|
||||
dt = g_date_time_new_now_local();
|
||||
|
||||
GDateTime *dt, *midnight;
|
||||
dt = g_date_time_new_now_local ();
|
||||
if (!is_first) {
|
||||
GDateTime* tmp = dt;
|
||||
dt = g_date_time_add_days(dt, 1);
|
||||
g_date_time_unref(tmp);
|
||||
}
|
||||
|
||||
if (!is_first) {
|
||||
GDateTime *tmp = dt;
|
||||
dt = g_date_time_add_days (dt, 1);
|
||||
g_date_time_unref (tmp);
|
||||
}
|
||||
midnight = g_date_time_add_full(dt,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
-g_date_time_get_hour(dt),
|
||||
-g_date_time_get_minute(dt),
|
||||
-g_date_time_get_second(dt));
|
||||
time_t t = MAX(0, (gint64)g_date_time_to_unix(midnight));
|
||||
g_date_time_unref(dt);
|
||||
g_date_time_unref(midnight);
|
||||
return date_to_time_t_string((time_t)t);
|
||||
|
||||
midnight = g_date_time_add_full (dt, 0, 0, 0,
|
||||
-g_date_time_get_hour(dt),
|
||||
-g_date_time_get_minute (dt),
|
||||
-g_date_time_get_second (dt));
|
||||
time_t t = MAX(0, (gint64)g_date_time_to_unix (midnight));
|
||||
g_date_time_unref (dt);
|
||||
g_date_time_unref (midnight);
|
||||
return date_to_time_t_string ((time_t)t);
|
||||
|
||||
} else
|
||||
return date_boundary (is_first);
|
||||
} else
|
||||
return date_boundary(is_first);
|
||||
}
|
||||
|
||||
// if a date has a month day greater than the number of days in that month,
|
||||
// change it to a valid date point to the last second in that month
|
||||
static void
|
||||
fixup_month (struct tm *tbuf)
|
||||
fixup_month(struct tm* tbuf)
|
||||
{
|
||||
decltype(tbuf->tm_mday) max_days;
|
||||
const auto month = tbuf->tm_mon + 1;
|
||||
const auto year = tbuf->tm_year + 1900;
|
||||
decltype(tbuf->tm_mday) max_days;
|
||||
const auto month = tbuf->tm_mon + 1;
|
||||
const auto year = tbuf->tm_year + 1900;
|
||||
|
||||
switch (month) {
|
||||
case 2:
|
||||
if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
|
||||
max_days = 29;
|
||||
else
|
||||
max_days = 28;
|
||||
break;
|
||||
case 4:
|
||||
case 6:
|
||||
case 9:
|
||||
case 11:
|
||||
max_days = 30;
|
||||
break;
|
||||
default:
|
||||
max_days = 31;
|
||||
break;
|
||||
}
|
||||
switch (month) {
|
||||
case 2:
|
||||
if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
|
||||
max_days = 29;
|
||||
else
|
||||
max_days = 28;
|
||||
break;
|
||||
case 4:
|
||||
case 6:
|
||||
case 9:
|
||||
case 11: max_days = 30; break;
|
||||
default: max_days = 31; break;
|
||||
}
|
||||
|
||||
if (tbuf->tm_mday > max_days) {
|
||||
tbuf->tm_mday = max_days;
|
||||
tbuf->tm_hour = 23;
|
||||
tbuf->tm_min = 59;
|
||||
tbuf->tm_sec = 59;
|
||||
}
|
||||
if (tbuf->tm_mday > max_days) {
|
||||
tbuf->tm_mday = max_days;
|
||||
tbuf->tm_hour = 23;
|
||||
tbuf->tm_min = 59;
|
||||
tbuf->tm_sec = 59;
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
Mu::date_to_time_t_string (const std::string& dstr, bool is_first)
|
||||
Mu::date_to_time_t_string(const std::string& dstr, bool is_first)
|
||||
{
|
||||
gint64 t;
|
||||
struct tm tbuf;
|
||||
GDateTime *dtime;
|
||||
gint64 t;
|
||||
struct tm tbuf;
|
||||
GDateTime* dtime;
|
||||
|
||||
/* one-sided dates */
|
||||
if (dstr.empty())
|
||||
return date_boundary (is_first);
|
||||
else if (dstr == "today" || dstr == "now")
|
||||
return special_date (dstr, is_first);
|
||||
/* one-sided dates */
|
||||
if (dstr.empty())
|
||||
return date_boundary(is_first);
|
||||
else if (dstr == "today" || dstr == "now")
|
||||
return special_date(dstr, is_first);
|
||||
|
||||
else if (dstr.find_first_of("ymdwhMs") != std::string::npos)
|
||||
return delta_ymwdhMs (dstr);
|
||||
else if (dstr.find_first_of("ymdwhMs") != std::string::npos)
|
||||
return delta_ymwdhMs(dstr);
|
||||
|
||||
constexpr char UserDateMin[] = "19700101000000";
|
||||
constexpr char UserDateMax[] = "29991231235959";
|
||||
constexpr char UserDateMin[] = "19700101000000";
|
||||
constexpr char UserDateMax[] = "29991231235959";
|
||||
|
||||
std::string date (is_first ? UserDateMin : UserDateMax);
|
||||
std::copy_if (dstr.begin(), dstr.end(), date.begin(),[](auto c){return isdigit(c);});
|
||||
std::string date(is_first ? UserDateMin : UserDateMax);
|
||||
std::copy_if(dstr.begin(), dstr.end(), date.begin(), [](auto c) { return isdigit(c); });
|
||||
|
||||
memset (&tbuf, 0, sizeof tbuf);
|
||||
if (!strptime (date.c_str(), "%Y%m%d%H%M%S", &tbuf) &&
|
||||
!strptime (date.c_str(), "%Y%m%d%H%M", &tbuf) &&
|
||||
!strptime (date.c_str(), "%Y%m%d", &tbuf) &&
|
||||
!strptime (date.c_str(), "%Y%m", &tbuf) &&
|
||||
!strptime (date.c_str(), "%Y", &tbuf))
|
||||
return date_boundary (is_first);
|
||||
memset(&tbuf, 0, sizeof tbuf);
|
||||
if (!strptime(date.c_str(), "%Y%m%d%H%M%S", &tbuf) &&
|
||||
!strptime(date.c_str(), "%Y%m%d%H%M", &tbuf) &&
|
||||
!strptime(date.c_str(), "%Y%m%d", &tbuf) && !strptime(date.c_str(), "%Y%m", &tbuf) &&
|
||||
!strptime(date.c_str(), "%Y", &tbuf))
|
||||
return date_boundary(is_first);
|
||||
|
||||
fixup_month(&tbuf);
|
||||
fixup_month(&tbuf);
|
||||
|
||||
dtime = g_date_time_new_local (tbuf.tm_year + 1900,
|
||||
tbuf.tm_mon + 1,
|
||||
tbuf.tm_mday,
|
||||
tbuf.tm_hour,
|
||||
tbuf.tm_min,
|
||||
tbuf.tm_sec);
|
||||
if (!dtime) {
|
||||
g_warning ("invalid %s date '%s'",
|
||||
is_first ? "lower" : "upper", date.c_str());
|
||||
return date_boundary (is_first);
|
||||
}
|
||||
dtime = g_date_time_new_local(tbuf.tm_year + 1900,
|
||||
tbuf.tm_mon + 1,
|
||||
tbuf.tm_mday,
|
||||
tbuf.tm_hour,
|
||||
tbuf.tm_min,
|
||||
tbuf.tm_sec);
|
||||
if (!dtime) {
|
||||
g_warning("invalid %s date '%s'", is_first ? "lower" : "upper", date.c_str());
|
||||
return date_boundary(is_first);
|
||||
}
|
||||
|
||||
t = g_date_time_to_unix (dtime);
|
||||
g_date_time_unref (dtime);
|
||||
t = g_date_time_to_unix(dtime);
|
||||
g_date_time_unref(dtime);
|
||||
|
||||
if (t < 0 || t > 9999999999)
|
||||
return date_boundary (is_first);
|
||||
else
|
||||
return date_to_time_t_string (t);
|
||||
if (t < 0 || t > 9999999999)
|
||||
return date_boundary(is_first);
|
||||
else
|
||||
return date_to_time_t_string(t);
|
||||
}
|
||||
|
||||
constexpr const auto SizeFormat = "%010" G_GINT64_FORMAT;
|
||||
@ -412,98 +401,91 @@ static_assert(sizeof(SizeMin) == 10 + 1, "invalid");
|
||||
static_assert(sizeof(SizeMax) == 10 + 1, "invalid");
|
||||
|
||||
static std::string
|
||||
size_boundary (bool is_first)
|
||||
size_boundary(bool is_first)
|
||||
{
|
||||
return is_first ? SizeMin : SizeMax;
|
||||
return is_first ? SizeMin : SizeMax;
|
||||
}
|
||||
|
||||
std::string
|
||||
Mu::size_to_string (int64_t size)
|
||||
Mu::size_to_string(int64_t size)
|
||||
{
|
||||
char buf[sizeof(SizeMax)];
|
||||
g_snprintf (buf, sizeof(buf), SizeFormat, size);
|
||||
char buf[sizeof(SizeMax)];
|
||||
g_snprintf(buf, sizeof(buf), SizeFormat, size);
|
||||
|
||||
return buf;
|
||||
return buf;
|
||||
}
|
||||
|
||||
std::string
|
||||
Mu::size_to_string (const std::string& val, bool is_first)
|
||||
Mu::size_to_string(const std::string& val, bool is_first)
|
||||
{
|
||||
std::string str;
|
||||
GRegex *rx;
|
||||
GMatchInfo *minfo;
|
||||
std::string str;
|
||||
GRegex* rx;
|
||||
GMatchInfo* minfo;
|
||||
|
||||
/* one-sided ranges */
|
||||
if (val.empty())
|
||||
return size_boundary (is_first);
|
||||
/* one-sided ranges */
|
||||
if (val.empty())
|
||||
return size_boundary(is_first);
|
||||
|
||||
rx = g_regex_new ("(\\d+)(b|k|kb|m|mb|g|gb)?",
|
||||
G_REGEX_CASELESS, (GRegexMatchFlags)0, NULL);
|
||||
minfo = NULL;
|
||||
if (g_regex_match (rx, val.c_str(), (GRegexMatchFlags)0, &minfo)) {
|
||||
gint64 size;
|
||||
char *s;
|
||||
rx = g_regex_new("(\\d+)(b|k|kb|m|mb|g|gb)?", G_REGEX_CASELESS, (GRegexMatchFlags)0, NULL);
|
||||
minfo = NULL;
|
||||
if (g_regex_match(rx, val.c_str(), (GRegexMatchFlags)0, &minfo)) {
|
||||
gint64 size;
|
||||
char* s;
|
||||
|
||||
s = g_match_info_fetch (minfo, 1);
|
||||
size = atoll (s);
|
||||
g_free (s);
|
||||
s = g_match_info_fetch(minfo, 1);
|
||||
size = atoll(s);
|
||||
g_free(s);
|
||||
|
||||
s = g_match_info_fetch (minfo, 2);
|
||||
switch (s ? g_ascii_tolower(s[0]) : 0) {
|
||||
case 'k': size *= 1024; break;
|
||||
case 'm': size *= (1024 * 1024); break;
|
||||
case 'g': size *= (1024 * 1024 * 1024); break;
|
||||
default: break;
|
||||
}
|
||||
s = g_match_info_fetch(minfo, 2);
|
||||
switch (s ? g_ascii_tolower(s[0]) : 0) {
|
||||
case 'k': size *= 1024; break;
|
||||
case 'm': size *= (1024 * 1024); break;
|
||||
case 'g': size *= (1024 * 1024 * 1024); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
g_free (s);
|
||||
str = size_to_string (size);
|
||||
} else
|
||||
str = size_boundary (is_first);
|
||||
g_free(s);
|
||||
str = size_to_string(size);
|
||||
} else
|
||||
str = size_boundary(is_first);
|
||||
|
||||
g_regex_unref (rx);
|
||||
g_match_info_unref (minfo);
|
||||
g_regex_unref(rx);
|
||||
g_match_info_unref(minfo);
|
||||
|
||||
return str;
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
Mu::canonicalize_filename(const std::string& path, const std::string& relative_to)
|
||||
{
|
||||
char *fname = mu_canonicalize_filename (
|
||||
path.c_str(),
|
||||
relative_to.empty() ? NULL : relative_to.c_str());
|
||||
char* fname = mu_canonicalize_filename(path.c_str(),
|
||||
relative_to.empty() ? NULL : relative_to.c_str());
|
||||
|
||||
std::string rv{fname};
|
||||
g_free (fname);
|
||||
std::string rv{fname};
|
||||
g_free(fname);
|
||||
|
||||
return rv;
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Mu::assert_equal(const std::string& s1, const std::string& s2)
|
||||
{
|
||||
g_assert_cmpstr (s1.c_str(), ==, s2.c_str());
|
||||
g_assert_cmpstr(s1.c_str(), ==, s2.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
Mu::assert_equal (const Mu::StringVec& v1, const Mu::StringVec& v2)
|
||||
Mu::assert_equal(const Mu::StringVec& v1, const Mu::StringVec& v2)
|
||||
{
|
||||
g_assert_cmpuint(v1.size(), ==, v2.size());
|
||||
g_assert_cmpuint(v1.size(), ==, v2.size());
|
||||
|
||||
for (auto i = 0U; i != v1.size(); ++i)
|
||||
assert_equal(v1[i], v2[i]);
|
||||
for (auto i = 0U; i != v1.size(); ++i)
|
||||
assert_equal(v1[i], v2[i]);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Mu::allow_warnings()
|
||||
{
|
||||
g_test_log_set_fatal_handler(
|
||||
[](const char*, GLogLevelFlags, const char*, gpointer) {
|
||||
return FALSE;
|
||||
},{});
|
||||
|
||||
g_test_log_set_fatal_handler(
|
||||
[](const char*, GLogLevelFlags, const char*, gpointer) { return FALSE; },
|
||||
{});
|
||||
}
|
||||
|
||||
@ -42,8 +42,12 @@ using StringVec = std::vector<std::string>;
|
||||
*
|
||||
* @return a flattened string
|
||||
*/
|
||||
std::string utf8_flatten (const char *str);
|
||||
inline std::string utf8_flatten (const std::string& s) { return utf8_flatten(s.c_str()); }
|
||||
std::string utf8_flatten(const char* str);
|
||||
inline std::string
|
||||
utf8_flatten(const std::string& s)
|
||||
{
|
||||
return utf8_flatten(s.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace all control characters with spaces, and remove leading and trailing space.
|
||||
@ -52,8 +56,7 @@ inline std::string utf8_flatten (const std::string& s) { return utf8_flatten(s.c
|
||||
*
|
||||
* @return a cleaned-up string.
|
||||
*/
|
||||
std::string utf8_clean (const std::string& dirty);
|
||||
|
||||
std::string utf8_clean(const std::string& dirty);
|
||||
|
||||
/**
|
||||
* Remove ctrl characters, replacing them with ' '; subsequent
|
||||
@ -63,8 +66,7 @@ std::string utf8_clean (const std::string& dirty);
|
||||
*
|
||||
* @return the string without control characters
|
||||
*/
|
||||
std::string remove_ctrl (const std::string& str);
|
||||
|
||||
std::string remove_ctrl(const std::string& str);
|
||||
|
||||
/**
|
||||
* Split a string in parts
|
||||
@ -74,8 +76,7 @@ std::string remove_ctrl (const std::string& str);
|
||||
*
|
||||
* @return the parts.
|
||||
*/
|
||||
std::vector<std::string> split (const std::string& str,
|
||||
const std::string& sepa);
|
||||
std::vector<std::string> split(const std::string& str, const std::string& sepa);
|
||||
|
||||
/**
|
||||
* Quote & escape a string for " and \
|
||||
@ -84,7 +85,7 @@ std::vector<std::string> split (const std::string& str,
|
||||
*
|
||||
* @return quoted string
|
||||
*/
|
||||
std::string quote (const std::string& str);
|
||||
std::string quote(const std::string& str);
|
||||
|
||||
/**
|
||||
* Format a string, printf style
|
||||
@ -94,7 +95,7 @@ std::string quote (const std::string& str);
|
||||
*
|
||||
* @return a formatted string
|
||||
*/
|
||||
std::string format (const char *frm, ...) __attribute__((format(printf, 1, 2)));
|
||||
std::string format(const char* frm, ...) __attribute__((format(printf, 1, 2)));
|
||||
|
||||
/**
|
||||
* Format a string, printf style
|
||||
@ -104,8 +105,7 @@ std::string format (const char *frm, ...) __attribute__((format(printf, 1, 2)));
|
||||
*
|
||||
* @return a formatted string
|
||||
*/
|
||||
std::string vformat (const char *frm, va_list args) __attribute__((format(printf, 1, 0)));
|
||||
|
||||
std::string vformat(const char* frm, va_list args) __attribute__((format(printf, 1, 0)));
|
||||
|
||||
/**
|
||||
* Convert an date to the corresponding time expressed as a string with a
|
||||
@ -118,7 +118,7 @@ std::string vformat (const char *frm, va_list args) __attribute__((format(printf
|
||||
*
|
||||
* @return the corresponding time_t expressed as a string
|
||||
*/
|
||||
std::string date_to_time_t_string (const std::string& date, bool first);
|
||||
std::string date_to_time_t_string(const std::string& date, bool first);
|
||||
|
||||
/**
|
||||
* 64-bit incarnation of time_t expressed as a 10-digit string. Uses 64-bit for the time-value,
|
||||
@ -128,37 +128,52 @@ std::string date_to_time_t_string (const std::string& date, bool first);
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
std::string date_to_time_t_string (int64_t t);
|
||||
std::string date_to_time_t_string(int64_t t);
|
||||
|
||||
using Clock = std::chrono::steady_clock;
|
||||
using Duration = Clock::duration;
|
||||
|
||||
template <typename Unit> constexpr int64_t to_unit (Duration d) {
|
||||
using namespace std::chrono;
|
||||
return duration_cast<Unit>(d).count();
|
||||
template <typename Unit>
|
||||
constexpr int64_t
|
||||
to_unit(Duration d)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
return duration_cast<Unit>(d).count();
|
||||
}
|
||||
|
||||
constexpr int64_t to_s (Duration d) { return to_unit<std::chrono::seconds>(d); }
|
||||
constexpr int64_t to_ms (Duration d) { return to_unit<std::chrono::milliseconds>(d); }
|
||||
constexpr int64_t to_us (Duration d) { return to_unit<std::chrono::microseconds>(d); }
|
||||
constexpr int64_t
|
||||
to_s(Duration d)
|
||||
{
|
||||
return to_unit<std::chrono::seconds>(d);
|
||||
}
|
||||
constexpr int64_t
|
||||
to_ms(Duration d)
|
||||
{
|
||||
return to_unit<std::chrono::milliseconds>(d);
|
||||
}
|
||||
constexpr int64_t
|
||||
to_us(Duration d)
|
||||
{
|
||||
return to_unit<std::chrono::microseconds>(d);
|
||||
}
|
||||
|
||||
struct StopWatch {
|
||||
using Clock = std::chrono::steady_clock;
|
||||
StopWatch (const std::string name):
|
||||
start_{Clock::now()},
|
||||
name_{name} {}
|
||||
~StopWatch() {
|
||||
const auto us{to_us(Clock::now()-start_)};
|
||||
if (us > 2000000)
|
||||
g_debug ("%s: finished after %0.1f s", name_.c_str(), us/1000000.0);
|
||||
else if (us > 2000)
|
||||
g_debug ("%s: finished after %0.1f ms", name_.c_str(), us/1000.0);
|
||||
else
|
||||
g_debug ("%s: finished after %" G_GINT64_FORMAT " us", name_.c_str(), us);
|
||||
}
|
||||
private:
|
||||
Clock::time_point start_;
|
||||
std::string name_;
|
||||
using Clock = std::chrono::steady_clock;
|
||||
StopWatch(const std::string name) : start_{Clock::now()}, name_{name} {}
|
||||
~StopWatch()
|
||||
{
|
||||
const auto us{to_us(Clock::now() - start_)};
|
||||
if (us > 2000000)
|
||||
g_debug("%s: finished after %0.1f s", name_.c_str(), us / 1000000.0);
|
||||
else if (us > 2000)
|
||||
g_debug("%s: finished after %0.1f ms", name_.c_str(), us / 1000.0);
|
||||
else
|
||||
g_debug("%s: finished after %" G_GINT64_FORMAT " us", name_.c_str(), us);
|
||||
}
|
||||
|
||||
private:
|
||||
Clock::time_point start_;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -179,7 +194,7 @@ std::string canonicalize_filename(const std::string& path, const std::string& re
|
||||
*
|
||||
* @return the size expressed as a string with the decimal number of bytes
|
||||
*/
|
||||
std::string size_to_string (const std::string& sizestr, bool first);
|
||||
std::string size_to_string(const std::string& sizestr, bool first);
|
||||
|
||||
/**
|
||||
* Convert a size into a size in bytes string
|
||||
@ -189,8 +204,7 @@ std::string size_to_string (const std::string& sizestr, bool first);
|
||||
*
|
||||
* @return the size expressed as a string with the decimal number of bytes
|
||||
*/
|
||||
std::string size_to_string (int64_t size);
|
||||
|
||||
std::string size_to_string(int64_t size);
|
||||
|
||||
/**
|
||||
* Convert any ostreamable<< value to a string
|
||||
@ -200,90 +214,98 @@ std::string size_to_string (int64_t size);
|
||||
* @return a std::string
|
||||
*/
|
||||
template <typename T>
|
||||
static inline std::string to_string (const T& val)
|
||||
static inline std::string
|
||||
to_string(const T& val)
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << val;
|
||||
std::stringstream sstr;
|
||||
sstr << val;
|
||||
|
||||
return sstr.str();
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
|
||||
struct MaybeAnsi {
|
||||
explicit MaybeAnsi(bool use_color): color_{use_color} {}
|
||||
explicit MaybeAnsi(bool use_color) : color_{use_color} {}
|
||||
|
||||
enum struct Color {
|
||||
Black = 30,
|
||||
Red = 31,
|
||||
Green = 32,
|
||||
Yellow = 33,
|
||||
Blue = 34,
|
||||
Magenta = 35,
|
||||
Cyan = 36,
|
||||
White = 37,
|
||||
enum struct Color {
|
||||
Black = 30,
|
||||
Red = 31,
|
||||
Green = 32,
|
||||
Yellow = 33,
|
||||
Blue = 34,
|
||||
Magenta = 35,
|
||||
Cyan = 36,
|
||||
White = 37,
|
||||
|
||||
BrightBlack = 90,
|
||||
BrightRed = 91,
|
||||
BrightGreen = 92,
|
||||
BrightYellow = 93,
|
||||
BrightBlue = 94,
|
||||
BrightMagenta = 95,
|
||||
BrightCyan = 96,
|
||||
BrightWhite = 97,
|
||||
};
|
||||
BrightBlack = 90,
|
||||
BrightRed = 91,
|
||||
BrightGreen = 92,
|
||||
BrightYellow = 93,
|
||||
BrightBlue = 94,
|
||||
BrightMagenta = 95,
|
||||
BrightCyan = 96,
|
||||
BrightWhite = 97,
|
||||
};
|
||||
|
||||
std::string fg(Color c) const { return ansi(c, true); }
|
||||
std::string bg(Color c) const { return ansi(c, false); }
|
||||
std::string fg(Color c) const { return ansi(c, true); }
|
||||
std::string bg(Color c) const { return ansi(c, false); }
|
||||
|
||||
std::string reset() const { return color_ ? "\x1b[0m" : ""; }
|
||||
std::string reset() const { return color_ ? "\x1b[0m" : ""; }
|
||||
|
||||
private:
|
||||
std::string ansi(Color c, bool fg=true) const {
|
||||
return color_ ? format("\x1b[%dm", static_cast<int>(c) + (fg ? 0 : 10)) : "";
|
||||
}
|
||||
private:
|
||||
std::string ansi(Color c, bool fg = true) const
|
||||
{
|
||||
return color_ ? format("\x1b[%dm", static_cast<int>(c) + (fg ? 0 : 10)) : "";
|
||||
}
|
||||
|
||||
const bool color_;
|
||||
const bool color_;
|
||||
};
|
||||
|
||||
template <typename Func> void xapian_try(Func&& func) noexcept try {
|
||||
func();
|
||||
} catch (const Xapian::Error &xerr) {
|
||||
g_critical ("%s: xapian error '%s'",
|
||||
__func__, xerr.get_msg().c_str());
|
||||
template <typename Func>
|
||||
void
|
||||
xapian_try(Func&& func) noexcept
|
||||
try {
|
||||
func();
|
||||
} catch (const Xapian::Error& xerr) {
|
||||
g_critical("%s: xapian error '%s'", __func__, xerr.get_msg().c_str());
|
||||
} catch (const std::runtime_error& re) {
|
||||
g_critical ("%s: error: %s", __func__, re.what());
|
||||
g_critical("%s: error: %s", __func__, re.what());
|
||||
} catch (...) {
|
||||
g_critical ("%s: caught exception", __func__);
|
||||
g_critical("%s: caught exception", __func__);
|
||||
}
|
||||
|
||||
template <typename Func, typename Default=std::invoke_result<Func>>
|
||||
auto xapian_try(Func&& func, Default&& def) noexcept -> std::decay_t<decltype(func())> try {
|
||||
return func();
|
||||
} catch (const Xapian::Error &xerr) {
|
||||
g_critical ("%s: xapian error '%s'",
|
||||
__func__, xerr.get_msg().c_str());
|
||||
template <typename Func, typename Default = std::invoke_result<Func>>
|
||||
auto
|
||||
xapian_try(Func&& func, Default&& def) noexcept -> std::decay_t<decltype(func())>
|
||||
try {
|
||||
return func();
|
||||
} catch (const Xapian::Error& xerr) {
|
||||
g_critical("%s: xapian error '%s'", __func__, xerr.get_msg().c_str());
|
||||
return static_cast<Default>(def);
|
||||
} catch (const std::runtime_error& re) {
|
||||
g_critical ("%s: error: %s", __func__, re.what());
|
||||
g_critical("%s: error: %s", __func__, re.what());
|
||||
return static_cast<Default>(def);
|
||||
} catch (...) {
|
||||
g_critical ("%s: caught exception", __func__);
|
||||
g_critical("%s: caught exception", __func__);
|
||||
return static_cast<Default>(def);
|
||||
}
|
||||
|
||||
|
||||
/// Allow using enum structs as bitflags
|
||||
#define MU_TO_NUM(ET,ELM) std::underlying_type_t<ET>(ELM)
|
||||
#define MU_TO_ENUM(ET,NUM) static_cast<ET>(NUM)
|
||||
#define MU_ENABLE_BITOPS(ET) \
|
||||
constexpr ET operator& (ET e1, ET e2) { return MU_TO_ENUM(ET,MU_TO_NUM(ET,e1)&MU_TO_NUM(ET,e2)); } \
|
||||
constexpr ET operator| (ET e1, ET e2) { return MU_TO_ENUM(ET,MU_TO_NUM(ET,e1)|MU_TO_NUM(ET,e2)); } \
|
||||
constexpr ET operator~ (ET e) { return MU_TO_ENUM(ET,~(MU_TO_NUM(ET, e))); } \
|
||||
constexpr bool any_of(ET e) { return MU_TO_NUM(ET,e) != 0; } \
|
||||
constexpr bool none_of(ET e) { return MU_TO_NUM(ET,e) == 0; } \
|
||||
static inline ET& operator&=(ET& e1, ET e2) { return e1 = e1 & e2;} \
|
||||
static inline ET& operator|=(ET& e1, ET e2) { return e1 = e1 | e2;}
|
||||
|
||||
#define MU_TO_NUM(ET, ELM) std::underlying_type_t<ET>(ELM)
|
||||
#define MU_TO_ENUM(ET, NUM) static_cast<ET>(NUM)
|
||||
#define MU_ENABLE_BITOPS(ET) \
|
||||
constexpr ET operator&(ET e1, ET e2) \
|
||||
{ \
|
||||
return MU_TO_ENUM(ET, MU_TO_NUM(ET, e1) & MU_TO_NUM(ET, e2)); \
|
||||
} \
|
||||
constexpr ET operator|(ET e1, ET e2) \
|
||||
{ \
|
||||
return MU_TO_ENUM(ET, MU_TO_NUM(ET, e1) | MU_TO_NUM(ET, e2)); \
|
||||
} \
|
||||
constexpr ET operator~(ET e) { return MU_TO_ENUM(ET, ~(MU_TO_NUM(ET, e))); } \
|
||||
constexpr bool any_of(ET e) { return MU_TO_NUM(ET, e) != 0; } \
|
||||
constexpr bool none_of(ET e) { return MU_TO_NUM(ET, e) == 0; } \
|
||||
static inline ET& operator&=(ET& e1, ET e2) { return e1 = e1 & e2; } \
|
||||
static inline ET& operator|=(ET& e1, ET e2) { return e1 = e1 | e2; }
|
||||
|
||||
/**
|
||||
* For unit tests, assert two std::string's are equal.
|
||||
@ -298,7 +320,7 @@ void assert_equal(const std::string& s1, const std::string& s2);
|
||||
* @param c1 container1
|
||||
* @param c2 container2
|
||||
*/
|
||||
void assert_equal (const StringVec& v1, const StringVec& v2);
|
||||
void assert_equal(const StringVec& v1, const StringVec& v2);
|
||||
|
||||
/**
|
||||
* For unit-tests, allow warnings in the current function.
|
||||
@ -308,5 +330,4 @@ void allow_warnings();
|
||||
|
||||
} // namespace Mu
|
||||
|
||||
|
||||
#endif /* __MU_UTILS_HH__ */
|
||||
|
||||
@ -31,120 +31,118 @@ using namespace Mu;
|
||||
static void
|
||||
test_param_getters()
|
||||
{
|
||||
const auto sexp { Sexp::make_parse(R"((foo :bar 123 :cuux "456" :boo nil :bah true))")};
|
||||
const auto sexp{Sexp::make_parse(R"((foo :bar 123 :cuux "456" :boo nil :bah true))")};
|
||||
|
||||
if (g_test_verbose())
|
||||
std::cout << sexp << "\n";
|
||||
if (g_test_verbose())
|
||||
std::cout << sexp << "\n";
|
||||
|
||||
g_assert_cmpint(Command::get_int_or(sexp.list(), ":bar"), ==, 123);
|
||||
assert_equal(Command::get_string_or(sexp.list(), ":bra", "bla"), "bla");
|
||||
assert_equal(Command::get_string_or(sexp.list(), ":cuux"), "456");
|
||||
g_assert_cmpint(Command::get_int_or(sexp.list(), ":bar"), ==, 123);
|
||||
assert_equal(Command::get_string_or(sexp.list(), ":bra", "bla"), "bla");
|
||||
assert_equal(Command::get_string_or(sexp.list(), ":cuux"), "456");
|
||||
|
||||
g_assert_true(Command::get_bool_or(sexp.list(),":boo") == false);
|
||||
g_assert_true(Command::get_bool_or(sexp.list(),":bah") == true);
|
||||
g_assert_true(Command::get_bool_or(sexp.list(), ":boo") == false);
|
||||
g_assert_true(Command::get_bool_or(sexp.list(), ":bah") == true);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
call (const Command::CommandMap& cmap, const std::string& str) try
|
||||
{
|
||||
const auto sexp{Sexp::make_parse(str)};
|
||||
invoke (cmap, sexp);
|
||||
call(const Command::CommandMap& cmap, const std::string& str)
|
||||
try {
|
||||
const auto sexp{Sexp::make_parse(str)};
|
||||
invoke(cmap, sexp);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
|
||||
} catch (const Error& err) {
|
||||
g_warning ("%s", err.what());
|
||||
return false;
|
||||
g_warning("%s", err.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
test_command()
|
||||
{
|
||||
using namespace Command;
|
||||
allow_warnings();
|
||||
using namespace Command;
|
||||
allow_warnings();
|
||||
|
||||
CommandMap cmap;
|
||||
CommandMap cmap;
|
||||
|
||||
cmap.emplace("my-command",
|
||||
CommandInfo{
|
||||
ArgMap{ {":param1", ArgInfo{Sexp::Type::String, true, "some string" }},
|
||||
{":param2", ArgInfo{Sexp::Type::Number, false, "some integer"}}},
|
||||
"My command,",
|
||||
{}});
|
||||
cmap.emplace(
|
||||
"my-command",
|
||||
CommandInfo{ArgMap{{":param1", ArgInfo{Sexp::Type::String, true, "some string"}},
|
||||
{":param2", ArgInfo{Sexp::Type::Number, false, "some integer"}}},
|
||||
"My command,",
|
||||
{}});
|
||||
|
||||
g_assert_true(call(cmap, "(my-command :param1 \"hello\")"));
|
||||
g_assert_true(call(cmap, "(my-command :param1 \"hello\" :param2 123)"));
|
||||
g_assert_true(call(cmap, "(my-command :param1 \"hello\")"));
|
||||
g_assert_true(call(cmap, "(my-command :param1 \"hello\" :param2 123)"));
|
||||
|
||||
g_assert_false(call(cmap, "(my-command :param1 \"hello\" :param2 123 :param3 xxx)"));
|
||||
g_assert_false(call(cmap, "(my-command :param1 \"hello\" :param2 123 :param3 xxx)"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_command2()
|
||||
{
|
||||
using namespace Command;
|
||||
allow_warnings();
|
||||
using namespace Command;
|
||||
allow_warnings();
|
||||
|
||||
CommandMap cmap;
|
||||
cmap.emplace("bla",
|
||||
CommandInfo{
|
||||
ArgMap{
|
||||
{":foo", ArgInfo{Sexp::Type::Number, false, "foo"}},
|
||||
{":bar", ArgInfo{Sexp::Type::String, false, "bar"}},
|
||||
},"yeah",
|
||||
[&](const auto& params){}});
|
||||
CommandMap cmap;
|
||||
cmap.emplace("bla",
|
||||
CommandInfo{ArgMap{
|
||||
{":foo", ArgInfo{Sexp::Type::Number, false, "foo"}},
|
||||
{":bar", ArgInfo{Sexp::Type::String, false, "bar"}},
|
||||
},
|
||||
"yeah",
|
||||
[&](const auto& params) {}});
|
||||
|
||||
|
||||
g_assert_true (call(cmap, "(bla :foo nil)"));
|
||||
g_assert_false (call(cmap, "(bla :foo nil :bla nil)"));
|
||||
g_assert_true(call(cmap, "(bla :foo nil)"));
|
||||
g_assert_false(call(cmap, "(bla :foo nil :bla nil)"));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_command_fail()
|
||||
{
|
||||
using namespace Command;
|
||||
using namespace Command;
|
||||
|
||||
allow_warnings();
|
||||
allow_warnings();
|
||||
|
||||
CommandMap cmap;
|
||||
CommandMap cmap;
|
||||
|
||||
cmap.emplace("my-command",
|
||||
CommandInfo{
|
||||
ArgMap{ {":param1", ArgInfo{Sexp::Type::String, true, "some string" }},
|
||||
{":param2", ArgInfo{Sexp::Type::Number, false, "some integer"}}},
|
||||
"My command,",
|
||||
{}});
|
||||
cmap.emplace(
|
||||
"my-command",
|
||||
CommandInfo{ArgMap{{":param1", ArgInfo{Sexp::Type::String, true, "some string"}},
|
||||
{":param2", ArgInfo{Sexp::Type::Number, false, "some integer"}}},
|
||||
"My command,",
|
||||
{}});
|
||||
|
||||
g_assert_false (call(cmap, "(my-command)"));
|
||||
g_assert_false (call(cmap, "(my-command2)"));
|
||||
g_assert_false(call(cmap, "(my-command :param1 123 :param2 123)"));
|
||||
g_assert_false(call(cmap, "(my-command :param1 \"hello\" :param2 \"123\")"));
|
||||
g_assert_false(call(cmap, "(my-command)"));
|
||||
g_assert_false(call(cmap, "(my-command2)"));
|
||||
g_assert_false(call(cmap, "(my-command :param1 123 :param2 123)"));
|
||||
g_assert_false(call(cmap, "(my-command :param1 \"hello\" :param2 \"123\")"));
|
||||
}
|
||||
|
||||
|
||||
static void black_hole() {}
|
||||
static void
|
||||
black_hole()
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[]) try
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
main(int argc, char* argv[])
|
||||
try {
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/utils/command-parser/param-getters", test_param_getters);
|
||||
g_test_add_func ("/utils/command-parser/command", test_command);
|
||||
g_test_add_func ("/utils/command-parser/command2", test_command2);
|
||||
g_test_add_func ("/utils/command-parser/command-fail", test_command_fail);
|
||||
g_test_add_func("/utils/command-parser/param-getters", test_param_getters);
|
||||
g_test_add_func("/utils/command-parser/command", test_command);
|
||||
g_test_add_func("/utils/command-parser/command2", test_command2);
|
||||
g_test_add_func("/utils/command-parser/command-fail", test_command_fail);
|
||||
|
||||
g_log_set_handler (NULL,
|
||||
(GLogLevelFlags)(G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL|
|
||||
G_LOG_FLAG_RECURSION),
|
||||
(GLogFunc)black_hole, NULL);
|
||||
|
||||
|
||||
return g_test_run ();
|
||||
g_log_set_handler(
|
||||
NULL,
|
||||
(GLogLevelFlags)(G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION),
|
||||
(GLogFunc)black_hole,
|
||||
NULL);
|
||||
|
||||
return g_test_run();
|
||||
|
||||
} catch (const std::runtime_error& re) {
|
||||
std::cerr << re.what() << "\n";
|
||||
return 1;
|
||||
std::cerr << re.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -23,37 +23,37 @@
|
||||
using namespace Mu;
|
||||
|
||||
static Option<int>
|
||||
get_opt_int (bool b)
|
||||
get_opt_int(bool b)
|
||||
{
|
||||
if (b)
|
||||
return Some(123);
|
||||
else
|
||||
return Nothing;
|
||||
if (b)
|
||||
return Some(123);
|
||||
else
|
||||
return Nothing;
|
||||
}
|
||||
|
||||
static void
|
||||
test_option()
|
||||
{
|
||||
{
|
||||
const auto oi{get_opt_int(true)};
|
||||
g_assert_true(!!oi);
|
||||
g_assert_cmpint(oi.value(),==,123);
|
||||
}
|
||||
{
|
||||
const auto oi{get_opt_int(true)};
|
||||
g_assert_true(!!oi);
|
||||
g_assert_cmpint(oi.value(), ==, 123);
|
||||
}
|
||||
|
||||
{
|
||||
const auto oi{get_opt_int(false)};
|
||||
g_assert_false(!!oi);
|
||||
g_assert_false(oi.has_value());
|
||||
g_assert_cmpint(oi.value_or(456),==,456);
|
||||
}
|
||||
{
|
||||
const auto oi{get_opt_int(false)};
|
||||
g_assert_false(!!oi);
|
||||
g_assert_false(oi.has_value());
|
||||
g_assert_cmpint(oi.value_or(456), ==, 456);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/option/option", test_option);
|
||||
g_test_add_func("/option/option", test_option);
|
||||
|
||||
return g_test_run ();
|
||||
return g_test_run();
|
||||
}
|
||||
|
||||
@ -29,122 +29,122 @@
|
||||
using namespace Mu;
|
||||
|
||||
static bool
|
||||
check_parse (const std::string& expr, const std::string& expected)
|
||||
check_parse(const std::string& expr, const std::string& expected)
|
||||
{
|
||||
try {
|
||||
const auto parsed{to_string(Sexp::make_parse(expr))};
|
||||
assert_equal(parsed, expected);
|
||||
return true;
|
||||
try {
|
||||
const auto parsed{to_string(Sexp::make_parse(expr))};
|
||||
assert_equal(parsed, expected);
|
||||
return true;
|
||||
|
||||
} catch (const Error& err) {
|
||||
g_warning ("caught exception parsing '%s': %s", expr.c_str(), err.what());
|
||||
return false;
|
||||
}
|
||||
} catch (const Error& err) {
|
||||
g_warning("caught exception parsing '%s': %s", expr.c_str(), err.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_parser()
|
||||
{
|
||||
check_parse(":foo-123", ":foo-123");
|
||||
check_parse("foo", "foo");
|
||||
check_parse(R"(12345)", "12345");
|
||||
check_parse(R"(-12345)", "-12345");
|
||||
check_parse(R"((123 bar "cuux"))", "(123 bar \"cuux\")");
|
||||
check_parse(":foo-123", ":foo-123");
|
||||
check_parse("foo", "foo");
|
||||
check_parse(R"(12345)", "12345");
|
||||
check_parse(R"(-12345)", "-12345");
|
||||
check_parse(R"((123 bar "cuux"))", "(123 bar \"cuux\")");
|
||||
|
||||
check_parse(R"("foo\"bar\"cuux")", "\"foo\\\"bar\\\"cuux\"");
|
||||
check_parse(R"("foo\"bar\"cuux")", "\"foo\\\"bar\\\"cuux\"");
|
||||
|
||||
check_parse(R"("foo
|
||||
bar")", "\"foo\nbar\"");
|
||||
check_parse(R"("foo
|
||||
bar")",
|
||||
"\"foo\nbar\"");
|
||||
}
|
||||
|
||||
static void
|
||||
test_list()
|
||||
{
|
||||
const auto nstr{Sexp::make_string("foo")};
|
||||
g_assert_true(nstr.value() == "foo");
|
||||
g_assert_true(nstr.type() == Sexp::Type::String);
|
||||
assert_equal(nstr.to_sexp_string(), "\"foo\"");
|
||||
const auto nstr{Sexp::make_string("foo")};
|
||||
g_assert_true(nstr.value() == "foo");
|
||||
g_assert_true(nstr.type() == Sexp::Type::String);
|
||||
assert_equal(nstr.to_sexp_string(), "\"foo\"");
|
||||
|
||||
const auto nnum{Sexp::make_number(123)};
|
||||
g_assert_true(nnum.value() == "123");
|
||||
g_assert_true(nnum.type() == Sexp::Type::Number);
|
||||
assert_equal(nnum.to_sexp_string(), "123");
|
||||
const auto nnum{Sexp::make_number(123)};
|
||||
g_assert_true(nnum.value() == "123");
|
||||
g_assert_true(nnum.type() == Sexp::Type::Number);
|
||||
assert_equal(nnum.to_sexp_string(), "123");
|
||||
|
||||
const auto nsym{Sexp::make_symbol("blub")};
|
||||
g_assert_true(nsym.value() == "blub");
|
||||
g_assert_true(nsym.type() == Sexp::Type::Symbol);
|
||||
assert_equal(nsym.to_sexp_string(), "blub");
|
||||
const auto nsym{Sexp::make_symbol("blub")};
|
||||
g_assert_true(nsym.value() == "blub");
|
||||
g_assert_true(nsym.type() == Sexp::Type::Symbol);
|
||||
assert_equal(nsym.to_sexp_string(), "blub");
|
||||
|
||||
Sexp::List list;
|
||||
list .add(Sexp::make_string("foo"))
|
||||
.add(Sexp::make_number(123))
|
||||
.add(Sexp::make_symbol("blub"));
|
||||
Sexp::List list;
|
||||
list.add(Sexp::make_string("foo"))
|
||||
.add(Sexp::make_number(123))
|
||||
.add(Sexp::make_symbol("blub"));
|
||||
|
||||
const auto nlst = Sexp::make_list(std::move(list));
|
||||
g_assert_true(nlst.list().size() == 3);
|
||||
g_assert_true(nlst.type() == Sexp::Type::List);
|
||||
g_assert_true(nlst.list().at(1).value() == "123");
|
||||
const auto nlst = Sexp::make_list(std::move(list));
|
||||
g_assert_true(nlst.list().size() == 3);
|
||||
g_assert_true(nlst.type() == Sexp::Type::List);
|
||||
g_assert_true(nlst.list().at(1).value() == "123");
|
||||
|
||||
assert_equal(nlst.to_sexp_string(),"(\"foo\" 123 blub)");
|
||||
assert_equal(nlst.to_sexp_string(), "(\"foo\" 123 blub)");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_prop_list()
|
||||
{
|
||||
Sexp::List l1;
|
||||
l1.add_prop(":foo", Sexp::make_string("bar"));
|
||||
Sexp s2{Sexp::make_list(std::move(l1))};
|
||||
assert_equal(s2.to_sexp_string(), "(:foo \"bar\")");
|
||||
Sexp::List l1;
|
||||
l1.add_prop(":foo", Sexp::make_string("bar"));
|
||||
Sexp s2{Sexp::make_list(std::move(l1))};
|
||||
assert_equal(s2.to_sexp_string(), "(:foo \"bar\")");
|
||||
|
||||
|
||||
Sexp::List l2;
|
||||
const std::string x{"bar"};
|
||||
l2.add_prop(":foo", Sexp::make_string(x));
|
||||
l2.add_prop(":bar", Sexp::make_number(77));
|
||||
Sexp::List l3;
|
||||
l3.add_prop(":cuux", Sexp::make_list(std::move(l2)));
|
||||
Sexp s3{Sexp::make_list(std::move(l3))};
|
||||
assert_equal(s3.to_sexp_string(), "(:cuux (:foo \"bar\" :bar 77))");
|
||||
Sexp::List l2;
|
||||
const std::string x{"bar"};
|
||||
l2.add_prop(":foo", Sexp::make_string(x));
|
||||
l2.add_prop(":bar", Sexp::make_number(77));
|
||||
Sexp::List l3;
|
||||
l3.add_prop(":cuux", Sexp::make_list(std::move(l2)));
|
||||
Sexp s3{Sexp::make_list(std::move(l3))};
|
||||
assert_equal(s3.to_sexp_string(), "(:cuux (:foo \"bar\" :bar 77))");
|
||||
}
|
||||
|
||||
static void
|
||||
test_props()
|
||||
{
|
||||
auto sexp2 = Sexp::make_list(
|
||||
Sexp::make_string("foo"),
|
||||
Sexp::make_number(123),
|
||||
Sexp::make_symbol("blub"));
|
||||
auto sexp2 = Sexp::make_list(Sexp::make_string("foo"),
|
||||
Sexp::make_number(123),
|
||||
Sexp::make_symbol("blub"));
|
||||
|
||||
auto sexp = Sexp::make_prop_list(
|
||||
":foo", Sexp::make_string("bär"),
|
||||
":cuux", Sexp::make_number(123),
|
||||
":flub", Sexp::make_symbol("fnord"),
|
||||
":boo", std::move(sexp2));
|
||||
auto sexp = Sexp::make_prop_list(":foo",
|
||||
Sexp::make_string("bär"),
|
||||
":cuux",
|
||||
Sexp::make_number(123),
|
||||
":flub",
|
||||
Sexp::make_symbol("fnord"),
|
||||
":boo",
|
||||
std::move(sexp2));
|
||||
|
||||
assert_equal(sexp.to_sexp_string(),
|
||||
"(:foo \"b\303\244r\" :cuux 123 :flub fnord :boo (\"foo\" 123 blub))");
|
||||
assert_equal(sexp.to_sexp_string(),
|
||||
"(:foo \"b\303\244r\" :cuux 123 :flub fnord :boo (\"foo\" 123 blub))");
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[]) try
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
main(int argc, char* argv[])
|
||||
try {
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
if (argc == 2) {
|
||||
std::cout << Sexp::make_parse(argv[1]) << '\n';
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2) {
|
||||
std::cout << Sexp::make_parse(argv[1]) << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_test_add_func ("/utils/sexp/parser", test_parser);
|
||||
g_test_add_func ("/utils/sexp/list", test_list);
|
||||
g_test_add_func ("/utils/sexp/proplist", test_prop_list);
|
||||
g_test_add_func ("/utils/sexp/props", test_props);
|
||||
|
||||
return g_test_run ();
|
||||
g_test_add_func("/utils/sexp/parser", test_parser);
|
||||
g_test_add_func("/utils/sexp/list", test_list);
|
||||
g_test_add_func("/utils/sexp/proplist", test_prop_list);
|
||||
g_test_add_func("/utils/sexp/props", test_props);
|
||||
|
||||
return g_test_run();
|
||||
|
||||
} catch (const std::runtime_error& re) {
|
||||
std::cerr << re.what() << "\n";
|
||||
return 1;
|
||||
std::cerr << re.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -29,19 +29,17 @@
|
||||
using namespace Mu;
|
||||
|
||||
struct Case {
|
||||
const std::string expr;
|
||||
bool is_first{};
|
||||
const std::string expected;
|
||||
const std::string expr;
|
||||
bool is_first{};
|
||||
const std::string expected;
|
||||
};
|
||||
using CaseVec = std::vector<Case>;
|
||||
using CaseVec = std::vector<Case>;
|
||||
using ProcFunc = std::function<std::string(std::string, bool)>;
|
||||
|
||||
|
||||
static void
|
||||
test_cases(const CaseVec& cases, ProcFunc proc)
|
||||
{
|
||||
for (const auto& casus : cases ) {
|
||||
|
||||
for (const auto& casus : cases) {
|
||||
const auto res = proc(casus.expr, casus.is_first);
|
||||
if (g_test_verbose()) {
|
||||
std::cout << "\n";
|
||||
@ -50,176 +48,159 @@ test_cases(const CaseVec& cases, ProcFunc proc)
|
||||
std::cout << "got: '" << res << "'" << std::endl;
|
||||
}
|
||||
|
||||
g_assert_true (casus.expected == res);
|
||||
g_assert_true(casus.expected == res);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_date_basic ()
|
||||
test_date_basic()
|
||||
{
|
||||
g_setenv ("TZ", "Europe/Helsinki", TRUE);
|
||||
g_setenv("TZ", "Europe/Helsinki", TRUE);
|
||||
|
||||
CaseVec cases = {
|
||||
{ "2015-09-18T09:10:23", true, "1442556623" },
|
||||
{ "1972-12-14T09:10:23", true, "0093165023" },
|
||||
{ "1854-11-18T17:10:23", true, "0000000000" },
|
||||
CaseVec cases = {{"2015-09-18T09:10:23", true, "1442556623"},
|
||||
{"1972-12-14T09:10:23", true, "0093165023"},
|
||||
{"1854-11-18T17:10:23", true, "0000000000"},
|
||||
|
||||
{ "2000-02-31T09:10:23", true, "0951861599" },
|
||||
{ "2000-02-29T23:59:59", true, "0951861599" },
|
||||
{"2000-02-31T09:10:23", true, "0951861599"},
|
||||
{"2000-02-29T23:59:59", true, "0951861599"},
|
||||
|
||||
{ "2016", true, "1451599200" },
|
||||
{ "2016", false, "1483221599" },
|
||||
{"2016", true, "1451599200"},
|
||||
{"2016", false, "1483221599"},
|
||||
|
||||
{ "fnorb", true, "0000000000" },
|
||||
{ "fnorb", false, "9999999999" },
|
||||
{ "", false, "9999999999" },
|
||||
{ "", true, "0000000000" }
|
||||
};
|
||||
{"fnorb", true, "0000000000"},
|
||||
{"fnorb", false, "9999999999"},
|
||||
{"", false, "9999999999"},
|
||||
{"", true, "0000000000"}};
|
||||
|
||||
test_cases (cases, [](auto s, auto f){ return date_to_time_t_string(s,f); });
|
||||
test_cases(cases, [](auto s, auto f) { return date_to_time_t_string(s, f); });
|
||||
}
|
||||
|
||||
static void
|
||||
test_date_ymwdhMs (void)
|
||||
test_date_ymwdhMs(void)
|
||||
{
|
||||
struct {
|
||||
std::string expr;
|
||||
long diff;
|
||||
int tolerance;
|
||||
} tests[] = {
|
||||
{ "3h", 3 * 60 * 60, 1 },
|
||||
{ "21d", 21 * 24 * 60 * 60, 3600 + 1 },
|
||||
{ "2w", 2 * 7 * 24 * 60 * 60, 3600 + 1 },
|
||||
std::string expr;
|
||||
long diff;
|
||||
int tolerance;
|
||||
} tests[] = {{"3h", 3 * 60 * 60, 1},
|
||||
{"21d", 21 * 24 * 60 * 60, 3600 + 1},
|
||||
{"2w", 2 * 7 * 24 * 60 * 60, 3600 + 1},
|
||||
|
||||
{ "2y", 2 * 365 * 24 * 60 * 60, 24 * 3600 + 1 },
|
||||
{ "3m", 3 * 30 * 24 * 60 * 60, 3 * 24 * 3600 + 1 }
|
||||
};
|
||||
{"2y", 2 * 365 * 24 * 60 * 60, 24 * 3600 + 1},
|
||||
{"3m", 3 * 30 * 24 * 60 * 60, 3 * 24 * 3600 + 1}};
|
||||
|
||||
for (auto i = 0; i != G_N_ELEMENTS(tests); ++i) {
|
||||
const auto diff = time(NULL) -
|
||||
strtol(Mu::date_to_time_t_string(tests[i].expr, true).c_str(),
|
||||
NULL, 10);
|
||||
const auto diff =
|
||||
time(NULL) -
|
||||
strtol(Mu::date_to_time_t_string(tests[i].expr, true).c_str(), NULL, 10);
|
||||
if (g_test_verbose())
|
||||
std::cerr << tests[i].expr << ' '
|
||||
<< diff << ' '
|
||||
<< tests[i].diff << std::endl;
|
||||
std::cerr << tests[i].expr << ' ' << diff << ' ' << tests[i].diff
|
||||
<< std::endl;
|
||||
|
||||
g_assert_true (tests[i].diff - diff <= tests[i].tolerance);
|
||||
g_assert_true(tests[i].diff - diff <= tests[i].tolerance);
|
||||
}
|
||||
|
||||
g_assert_true (strtol(Mu::date_to_time_t_string("-1y", true).c_str(),
|
||||
NULL, 10) == 0);
|
||||
g_assert_true(strtol(Mu::date_to_time_t_string("-1y", true).c_str(), NULL, 10) == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
test_size ()
|
||||
test_size()
|
||||
{
|
||||
CaseVec cases = {
|
||||
{ "456", true, "0000000456" },
|
||||
{ "", false, "9999999999" },
|
||||
{ "", true, "0000000000" },
|
||||
{"456", true, "0000000456"},
|
||||
{"", false, "9999999999"},
|
||||
{"", true, "0000000000"},
|
||||
};
|
||||
|
||||
test_cases (cases, [](auto s, auto f){ return size_to_string(s,f); });
|
||||
test_cases(cases, [](auto s, auto f) { return size_to_string(s, f); });
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_flatten ()
|
||||
test_flatten()
|
||||
{
|
||||
CaseVec cases = {
|
||||
{ "Менделе́ев", true, "менделеев" },
|
||||
{ "", false, "" },
|
||||
{ "Ångström", true, "angstrom" },
|
||||
{"Менделе́ев", true, "менделеев"},
|
||||
{"", false, ""},
|
||||
{"Ångström", true, "angstrom"},
|
||||
};
|
||||
|
||||
test_cases (cases, [](auto s, auto f){ return utf8_flatten(s); });
|
||||
test_cases(cases, [](auto s, auto f) { return utf8_flatten(s); });
|
||||
}
|
||||
|
||||
static void
|
||||
test_remove_ctrl ()
|
||||
test_remove_ctrl()
|
||||
{
|
||||
CaseVec cases = {
|
||||
{ "Foo\n\nbar", true, "Foo bar" },
|
||||
{ "", false, "" },
|
||||
{ " ", false, " " },
|
||||
{ "Hello World ", false, "Hello World " },
|
||||
{ "Ångström", false, "Ångström" },
|
||||
{"Foo\n\nbar", true, "Foo bar"},
|
||||
{"", false, ""},
|
||||
{" ", false, " "},
|
||||
{"Hello World ", false, "Hello World "},
|
||||
{"Ångström", false, "Ångström"},
|
||||
};
|
||||
|
||||
test_cases (cases, [](auto s, auto f){ return remove_ctrl(s); });
|
||||
test_cases(cases, [](auto s, auto f) { return remove_ctrl(s); });
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
test_clean ()
|
||||
test_clean()
|
||||
{
|
||||
CaseVec cases = {
|
||||
{ "\t a\t\nb ", true, "a b" },
|
||||
{ "", false, "" },
|
||||
{ "Ångström", true, "Ångström" },
|
||||
{"\t a\t\nb ", true, "a b"},
|
||||
{"", false, ""},
|
||||
{"Ångström", true, "Ångström"},
|
||||
};
|
||||
|
||||
test_cases (cases, [](auto s, auto f){ return utf8_clean(s); });
|
||||
test_cases(cases, [](auto s, auto f) { return utf8_clean(s); });
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_format ()
|
||||
test_format()
|
||||
{
|
||||
g_assert_true (format ("hello %s", "world") ==
|
||||
"hello world");
|
||||
g_assert_true (format ("hello %s, %u", "world", 123) ==
|
||||
"hello world, 123");
|
||||
g_assert_true(format("hello %s", "world") == "hello world");
|
||||
g_assert_true(format("hello %s, %u", "world", 123) == "hello world, 123");
|
||||
}
|
||||
|
||||
|
||||
enum struct Bits { None = 0, Bit1 = 1 << 0, Bit2 = 1 << 1 };
|
||||
MU_ENABLE_BITOPS(Bits);
|
||||
|
||||
static void
|
||||
test_define_bitmap()
|
||||
{
|
||||
g_assert_cmpuint((guint)Bits::None,==,(guint)0);
|
||||
g_assert_cmpuint((guint)Bits::Bit1,==,(guint)1);
|
||||
g_assert_cmpuint((guint)Bits::Bit2,==,(guint)2);
|
||||
g_assert_cmpuint((guint)Bits::None, ==, (guint)0);
|
||||
g_assert_cmpuint((guint)Bits::Bit1, ==, (guint)1);
|
||||
g_assert_cmpuint((guint)Bits::Bit2, ==, (guint)2);
|
||||
|
||||
g_assert_cmpuint((guint)(Bits::Bit1|Bits::Bit2),==,(guint)3);
|
||||
g_assert_cmpuint((guint)(Bits::Bit1&Bits::Bit2),==,(guint)0);
|
||||
g_assert_cmpuint((guint)(Bits::Bit1 | Bits::Bit2), ==, (guint)3);
|
||||
g_assert_cmpuint((guint)(Bits::Bit1 & Bits::Bit2), ==, (guint)0);
|
||||
|
||||
g_assert_cmpuint((guint)(Bits::Bit1&(~Bits::Bit2)),==,(guint)1);
|
||||
g_assert_cmpuint((guint)(Bits::Bit1 & (~Bits::Bit2)), ==, (guint)1);
|
||||
|
||||
{
|
||||
Bits b{Bits::Bit1};
|
||||
b|=Bits::Bit2;
|
||||
g_assert_cmpuint((guint)b,==,(guint)3);
|
||||
b |= Bits::Bit2;
|
||||
g_assert_cmpuint((guint)b, ==, (guint)3);
|
||||
}
|
||||
|
||||
{
|
||||
Bits b{Bits::Bit1};
|
||||
b&=Bits::Bit1;
|
||||
g_assert_cmpuint((guint)b,==,(guint)1);
|
||||
b &= Bits::Bit1;
|
||||
g_assert_cmpuint((guint)b, ==, (guint)1);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/utils/date-basic", test_date_basic);
|
||||
g_test_add_func ("/utils/date-ymwdhMs", test_date_ymwdhMs);
|
||||
g_test_add_func ("/utils/size", test_size);
|
||||
g_test_add_func ("/utils/flatten", test_flatten);
|
||||
g_test_add_func ("/utils/remove-ctrl", test_remove_ctrl);
|
||||
g_test_add_func ("/utils/clean", test_clean);
|
||||
g_test_add_func ("/utils/format", test_format);
|
||||
g_test_add_func ("/utils/define-bitmap", test_define_bitmap);
|
||||
g_test_add_func("/utils/date-basic", test_date_basic);
|
||||
g_test_add_func("/utils/date-ymwdhMs", test_date_ymwdhMs);
|
||||
g_test_add_func("/utils/size", test_size);
|
||||
g_test_add_func("/utils/flatten", test_flatten);
|
||||
g_test_add_func("/utils/remove-ctrl", test_remove_ctrl);
|
||||
g_test_add_func("/utils/clean", test_clean);
|
||||
g_test_add_func("/utils/format", test_format);
|
||||
g_test_add_func("/utils/define-bitmap", test_define_bitmap);
|
||||
|
||||
return g_test_run ();
|
||||
return g_test_run();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user