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:
Dirk-Jan C. Binnema
2021-10-20 12:18:15 +03:00
parent 09935cc4b3
commit 3dd721d5a3
111 changed files with 13851 additions and 14579 deletions

View File

@ -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__ */

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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__*/

View File

@ -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*/
}

View File

@ -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

View File

@ -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__ */

View File

@ -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();
}

View File

@ -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__ */

View File

@ -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; },
{});
}

View File

@ -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__ */

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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();
}