Commit ab9c4d94 by Maarten L. Hekkelman

compiling again

parent e5eb6225
...@@ -215,7 +215,11 @@ set(project_sources ...@@ -215,7 +215,11 @@ set(project_sources
${PROJECT_SOURCE_DIR}/src/Symmetry.cpp ${PROJECT_SOURCE_DIR}/src/Symmetry.cpp
${PROJECT_SOURCE_DIR}/src/TlsParser.cpp ${PROJECT_SOURCE_DIR}/src/TlsParser.cpp
${PROJECT_SOURCE_DIR}/src/v2/category.cpp
${PROJECT_SOURCE_DIR}/src/v2/dictionary_parser.cpp ${PROJECT_SOURCE_DIR}/src/v2/dictionary_parser.cpp
${PROJECT_SOURCE_DIR}/src/v2/item.cpp
${PROJECT_SOURCE_DIR}/src/v2/parser.cpp
${PROJECT_SOURCE_DIR}/src/v2/row.cpp
${PROJECT_SOURCE_DIR}/src/v2/validate.cpp ${PROJECT_SOURCE_DIR}/src/v2/validate.cpp
) )
......
...@@ -37,119 +37,38 @@ namespace cif::v2 ...@@ -37,119 +37,38 @@ namespace cif::v2
// -------------------------------------------------------------------- // --------------------------------------------------------------------
template <typename Alloc> class category
class category_t
{ {
private:
// --------------------------------------------------------------------
// Internal storage, strictly forward linked list with minimal space
// requirements. Strings of size 7 or shorter are stored internally.
// Typically, more than 99% of the strings in an mmCIF file are less
// than 8 bytes in length.
struct item_value
{
item_value(uint16_t column_ix, uint16_t length)
: m_next(nullptr)
, m_column_ix(column_ix)
, m_length(length)
{
}
item_value() = delete;
item_value(const item_value &) = delete;
item_value &operator=(const item_value &) = delete;
item_value *m_next;
uint16_t m_column_ix;
uint16_t m_length;
union
{
char m_local_data[8];
char *m_data;
};
static constexpr size_t kBufferSize = sizeof(m_local_data);
std::string_view text() const
{
return {m_length >= kBufferSize ? m_data : m_local_data, m_length};
}
const char *c_str() const
{
return m_length >= kBufferSize ? m_data : m_local_data;
}
};
static_assert(sizeof(item_value) == 24, "sizeof(item_value) should be 24 bytes");
public: public:
using allocator_type = Alloc;
allocator_type get_allocator() const
{
return m_allocator;
}
template <typename>
friend class row_handle; friend class row_handle;
template <typename, typename...> template <typename, typename...>
friend class iterator_impl; friend class iterator_impl;
using value_type = row_handle<category_t>; using value_type = row_handle;
using reference = value_type; using reference = value_type;
using const_reference = const value_type; using const_reference = const value_type;
using iterator = iterator_impl<category_t>; using iterator = iterator_impl<category>;
using const_iterator = iterator_impl<const category_t>; using const_iterator = iterator_impl<const category>;
class row
{
public:
row() = default;
private:
template <typename>
friend class item_handle;
template <typename, typename...>
friend class iterator_impl;
friend class category_t; category() = default;
void append(item_value *iv) category(std::string_view name)
{ : m_name(name)
if (m_head == nullptr)
m_head = m_tail = iv;
else
m_tail = m_tail->m_next = iv;
}
row *m_next = nullptr;
item_value *m_head = nullptr, *m_tail = nullptr;
};
category_t() = default;
category_t(std::string_view name, const allocator_type &alloc = allocator_type())
: m_allocator(alloc)
, m_name(name)
{ {
} }
category_t(const category_t &rhs) category(const category &rhs)
: m_allocator(std::allocator_traits<allocator_type>::select_on_container_copy_construction(rhs.get_allocator())) : m_name(rhs.m_name)
, m_name(rhs.m_name)
, m_columns(rhs.m_columns) , m_columns(rhs.m_columns)
{ {
for (auto r = rhs.m_head; r != nullptr; r = r->m_next) for (auto r = rhs.m_head; r != nullptr; r = r->m_next)
insert_impl(end(), clone_row(*r)); insert_impl(end(), clone_row(*r));
} }
category_t(category_t &&rhs) category(category &&rhs)
: m_allocator(std::move(rhs.m_allocator)) : m_name(std::move(rhs.m_name))
, m_name(std::move(rhs.m_name))
, m_columns(std::move(rhs.m_columns)) , m_columns(std::move(rhs.m_columns))
, m_head(rhs.m_head) , m_head(rhs.m_head)
, m_tail(rhs.m_tail) , m_tail(rhs.m_tail)
...@@ -158,28 +77,13 @@ class category_t ...@@ -158,28 +77,13 @@ class category_t
rhs.m_tail = nullptr; rhs.m_tail = nullptr;
} }
template <typename Alloc2> category &operator=(const category &rhs)
category_t(const category_t &c, const Alloc2 &a)
: m_allocator(a)
, m_name(c.m_name)
{
}
template <typename Alloc2>
category_t(category_t &&c, const Alloc2 &a)
: m_allocator(a)
, m_name(std::move(c.m_name))
{
}
category_t &operator=(const category_t &rhs)
{ {
if (this != &rhs) if (this != &rhs)
{ {
if (not empty()) if (not empty())
clear(); clear();
m_allocator = std::allocator_traits<allocator_type>::select_on_container_copy_construction(rhs.get_allocator());
m_name = rhs.m_name; m_name = rhs.m_name;
m_columns = rhs.m_columns; m_columns = rhs.m_columns;
...@@ -190,14 +94,13 @@ class category_t ...@@ -190,14 +94,13 @@ class category_t
return *this; return *this;
} }
category_t &operator=(category_t &&rhs) category &operator=(category &&rhs)
{ {
if (this != &rhs) if (this != &rhs)
{ {
if (not empty()) if (not empty())
clear(); clear();
m_allocator = std::move(rhs.m_allocator);
m_name = std::move(rhs.m_name); m_name = std::move(rhs.m_name);
m_columns = std::move(rhs.m_columns); m_columns = std::move(rhs.m_columns);
...@@ -210,7 +113,7 @@ class category_t ...@@ -210,7 +113,7 @@ class category_t
return *this; return *this;
} }
~category_t() ~category()
{ {
clear(); clear();
} }
...@@ -226,7 +129,7 @@ class category_t ...@@ -226,7 +129,7 @@ class category_t
const_reference front() const const_reference front() const
{ {
return {*this, *m_head}; return {const_cast<category &>(*this), const_cast<row &>(*m_head)};
} }
reference back() reference back()
...@@ -236,7 +139,7 @@ class category_t ...@@ -236,7 +139,7 @@ class category_t
const_reference back() const const_reference back() const
{ {
return {*this, *m_tail}; return {const_cast<category &>(*this), const_cast<row &>(*m_tail)};
} }
iterator begin() iterator begin()
...@@ -282,31 +185,165 @@ class category_t ...@@ -282,31 +185,165 @@ class category_t
// -------------------------------------------------------------------- // --------------------------------------------------------------------
template <typename... Ts, typename... Ns> template <typename... Ts, typename... Ns>
iterator_proxy<const category_t, Ts...> rows(Ns... names) const iterator_proxy<const category, Ts...> rows(Ns... names) const
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return iterator_proxy<const category, Ts...>(*this, begin(), {names...});
}
template <typename... Ts, typename... Ns>
iterator_proxy<category, Ts...> rows(Ns... names)
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return iterator_proxy<category, Ts...>(*this, begin(), {names...});
}
// --------------------------------------------------------------------
conditional_iterator_proxy<category> find(condition &&cond)
{
return find(begin(), std::forward<condition>(cond));
}
conditional_iterator_proxy<category> find(iterator pos, condition &&cond)
{
return {*this, pos, std::forward<condition>(cond)};
}
conditional_iterator_proxy<const category> find(condition &&cond) const
{
return find(cbegin(), std::forward<condition>(cond));
}
conditional_iterator_proxy<const category> find(const_iterator pos, condition &&cond) const
{
return conditional_iterator_proxy<const category>{*this, pos, std::forward<condition>(cond)};
}
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<category, Ts...> find(condition &&cond, Ns... names)
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return find<Ts...>(cbegin(), std::forward<condition>(cond), std::forward<Ns>(names)...);
}
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<const category, Ts...> find(condition &&cond, Ns... names) const
{ {
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return"); static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return iterator_proxy<const category_t, Ts...>(*this, begin(), {names...}); return find<Ts...>(cbegin(), std::forward<condition>(cond), std::forward<Ns>(names)...);
} }
template <typename... Ts, typename... Ns> template <typename... Ts, typename... Ns>
iterator_proxy<category_t, Ts...> rows(Ns... names) conditional_iterator_proxy<category, Ts...> find(const_iterator pos, condition &&cond, Ns... names)
{ {
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return"); static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return iterator_proxy<category_t, Ts...>(*this, begin(), {names...}); return {*this, pos, std::forward<condition>(cond), std::forward<Ns>(names)...};
}
template <typename... Ts, typename... Ns>
conditional_iterator_proxy<const category, Ts...> find(const_iterator pos, condition &&cond, Ns... names) const
{
static_assert(sizeof...(Ts) == sizeof...(Ns), "The number of column titles should be equal to the number of types to return");
return {*this, pos, std::forward<condition>(cond), std::forward<Ns>(names)...};
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// if you only expect a single row
row_handle find1(condition &&cond)
{
return find1(begin(), std::forward<condition>(cond));
}
row_handle find1(iterator pos, condition &&cond)
{
auto h = find(pos, std::forward<condition>(cond));
void insert(const_iterator pos, const row &row) if (h.empty())
throw std::runtime_error("No hits found");
if (h.size() != 1)
throw std::runtime_error("Hit not unique");
return *h.begin();
}
const row_handle find1(condition &&cond) const
{
return find1(cbegin(), std::forward<condition>(cond));
}
const row_handle find1(const_iterator pos, condition &&cond) const
{
auto h = find(pos, std::forward<condition>(cond));
if (h.empty())
throw std::runtime_error("No hits found");
if (h.size() != 1)
throw std::runtime_error("Hit not unique");
return *h.begin();
}
template <typename T>
T find1(condition &&cond, const char *column) const
{ {
insert_impl(pos, row); return find1<T>(cbegin(), std::forward<condition>(cond), column);
} }
void insert(const_iterator pos, row &&row) template <typename T>
T find1(const_iterator pos, condition &&cond, const char *column) const
{ {
insert_impl(pos, std::move(row)); auto h = find<T>(pos, std::forward<condition>(cond), column);
if (h.empty())
throw std::runtime_error("No hits found");
if (h.size() != 1)
throw std::runtime_error("Hit not unique");
return std::get<0>(*h.begin());
} }
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find1(condition &&cond, Cs... columns) const
{
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of column titles should be equal to the number of types to return");
// static_assert(std::is_same_v<Cs, const char*>..., "The column names should be const char");
return find1<Ts...>(cbegin(), std::forward<condition>(cond), std::forward<Cs>(columns)...);
}
template <typename... Ts, typename... Cs, typename U = std::enable_if_t<sizeof...(Ts) != 1>>
std::tuple<Ts...> find1(const_iterator pos, condition &&cond, Cs... columns) const
{
static_assert(sizeof...(Ts) == sizeof...(Cs), "The number of column titles should be equal to the number of types to return");
auto h = find<Ts...>(pos, std::forward<condition>(cond), std::forward<Cs>(columns)...);
if (h.empty())
throw std::runtime_error("No hits found");
if (h.size() != 1)
throw std::runtime_error("Hit not unique");
return *h.begin();
}
bool exists(condition &&cond) const;
// --------------------------------------------------------------------
// void insert(const_iterator pos, const row &row)
// {
// insert_impl(pos, row);
// }
// void insert(const_iterator pos, row &&row)
// {
// insert_impl(pos, std::move(row));
// }
iterator emplace(std::initializer_list<item> items) iterator emplace(std::initializer_list<item> items)
{ {
return this->emplace(items.begin(), items.end()); return this->emplace(items.begin(), items.end());
...@@ -337,7 +374,7 @@ class category_t ...@@ -337,7 +374,7 @@ class category_t
// } // }
// if (not seen and iv->mMandatory) // if (not seen and iv->mMandatory)
// throw std::runtime_error("missing mandatory field " + col.mName + " for Category " + mName); // throw std::runtime_error("missing mandatory field " + col.mName + " for category " + mName);
// } // }
// if (mIndex != nullptr) // if (mIndex != nullptr)
...@@ -438,7 +475,7 @@ class category_t ...@@ -438,7 +475,7 @@ class category_t
// { // {
// item_validator = mCatValidator->getValidatorForItem(column_name); // item_validator = mCatValidator->getValidatorForItem(column_name);
// if (item_validator == nullptr) // if (item_validator == nullptr)
// m_validator->reportError("tag " + std::string(column_name) + " not allowed in Category " + mName, false); // m_validator->reportError("tag " + std::string(column_name) + " not allowed in category " + mName, false);
// } // }
m_columns.emplace_back(column_name, item_validator); m_columns.emplace_back(column_name, item_validator);
...@@ -448,177 +485,20 @@ class category_t ...@@ -448,177 +485,20 @@ class category_t
} }
private: private:
void update_value(row *row, size_t column, std::string_view value, bool updateLinked, bool validate = true) void update_value(row *row, size_t column, std::string_view value, bool updateLinked, bool validate = true);
{
auto &col = m_columns[column];
const char *oldValue = nullptr;
for (auto iv = row->m_head; iv != nullptr; iv = iv->m_next)
{
assert(iv != iv->m_next and (iv->m_next == nullptr or iv != iv->m_next->m_next));
if (iv->m_column_ix == column)
{
oldValue = iv->c_str();
break;
}
}
if (oldValue != nullptr and value == oldValue) // no need to update
return;
std::string oldStrValue = oldValue ? oldValue : "";
// // check the value
// if (col.m_validator and validate)
// (*col.m_validator)(value);
// If the field is part of the Key for this Category, remove it from the index
// before updating
bool reinsert = false;
// if (updateLinked and // an update of an Item's value
// cat->mIndex != nullptr and cat->keyFieldsByIndex().count(column))
// {
// reinsert = cat->mIndex->find(mData);
// if (reinsert)
// cat->mIndex->erase(mData);
// }
// first remove old value with cix
if (row->m_head == nullptr)
; // nothing to do
else if (row->m_head->m_column_ix == column)
{
auto iv = row->m_head;
row->m_head = iv->m_next;
iv->m_next = nullptr;
delete_item(iv);
}
else
{
for (auto iv = row->m_head; iv->m_next != nullptr; iv = iv->m_next)
{
if (iv->m_next->m_column_ix != column)
continue;
auto nv = iv->m_next;
iv->m_next = nv->m_next;
nv->m_next = nullptr;
delete_item(nv);
break;
}
}
if (not value.empty())
{
auto nv = create_item(column, value);
if (row->m_head == nullptr)
row->m_head = nv;
else
{
auto iv = row->m_head;
while (iv->m_next != nullptr)
iv = iv->m_next;
iv->m_next = nv;
}
}
// if (reinsert)
// cat->mIndex->insert(mData);
// // see if we need to update any child categories that depend on this value
// auto iv = col.m_validator;
// if (not skipUpdateLinked and iv != nullptr and mCascade)
// {
// for (auto &&[childCat, linked] : cat->mChildLinks)
// {
// if (find(linked->mParentKeys.begin(), linked->mParentKeys.end(), iv->mTag) == linked->mParentKeys.end())
// continue;
// Condition cond;
// std::string childTag;
// for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
// {
// std::string pk = linked->mParentKeys[ix];
// std::string ck = linked->mChildKeys[ix];
// // TODO add code to *NOT* test mandatory fields for Empty
// if (pk == iv->mTag)
// {
// childTag = ck;
// cond = std::move(cond) && Key(ck) == oldStrValue;
// }
// else
// {
// const char *pk_value = (*this)[pk].c_str();
// if (*pk_value == 0)
// cond = std::move(cond) && Key(ck) == Empty();
// else
// cond = std::move(cond) && ((Key(ck) == pk_value) or Key(ck) == Empty());
// }
// }
// auto rows = childCat->find(std::move(cond));
// if (rows.empty())
// continue;
// // if (cif::VERBOSE > 2)
// // {
// // std::cerr << "Parent: " << linked->mParentCategory << " Child: " << linked->mChildCategory << std::endl
// // << cond << std::endl;
// // }
// // Now, suppose there are already rows in child that conform to the new value,
// // we then skip this renam
// Condition cond_n;
// for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
// {
// std::string pk = linked->mParentKeys[ix];
// std::string ck = linked->mChildKeys[ix];
// // TODO add code to *NOT* test mandatory fields for Empty
// if (pk == iv->mTag)
// cond_n = std::move(cond_n) && Key(ck) == value;
// else
// {
// const char *pk_value = (*this)[pk].c_str();
// if (*pk_value == 0)
// cond_n = std::move(cond_n) && Key(ck) == Empty();
// else
// cond_n = std::move(cond_n) && ((Key(ck) == pk_value) or Key(ck) == Empty());
// }
// }
// auto rows_n = childCat->find(std::move(cond_n)); private:
// if (not rows_n.empty()) using allocator_type = std::allocator<void>;
// {
// if (cif::VERBOSE > 0)
// std::cerr << "Will not rename in child category since there are already rows that link to the parent" << std::endl;
// continue;
// }
// for (auto &cr : rows) constexpr allocator_type get_allocator() const
// cr.assign(childTag, value, false); {
// } return {};
// }
} }
private: using char_allocator_type = typename std::allocator_traits<allocator_type>::template rebind_alloc<char>;
using char_allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<char>;
using char_allocator_traits = std::allocator_traits<char_allocator_type>; using char_allocator_traits = std::allocator_traits<char_allocator_type>;
using item_allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<item_value>; using item_allocator_type = typename std::allocator_traits<allocator_type>::template rebind_alloc<item_value>;
using item_allocator_traits = std::allocator_traits<item_allocator_type>; using item_allocator_traits = std::allocator_traits<item_allocator_type>;
item_allocator_traits::pointer get_item() item_allocator_traits::pointer get_item()
...@@ -740,89 +620,13 @@ class category_t ...@@ -740,89 +620,13 @@ class category_t
}; };
// proxy methods for every insertion // proxy methods for every insertion
iterator insert_impl(const_iterator pos, row *n) iterator insert_impl(const_iterator pos, row *n);
{
assert(n != nullptr);
assert(n->m_next == nullptr);
if (n == nullptr)
throw std::runtime_error("Invalid pointer passed to insert");
// insert at end, most often this is the case
if (pos.m_current == nullptr)
{
if (m_head == nullptr)
m_tail = m_head = n;
else
m_tail = m_tail->m_next = n;
}
else
{
assert(m_head != nullptr);
if (pos.m_current == m_head)
m_head = n->m_next = m_head;
else
n = n->m_next = m_head->m_next;
}
return iterator(*this, n);
}
iterator erase_impl(const_iterator pos)
{
if (pos == cend())
return pos;
row *n = const_cast<row *>(&*pos);
row *cur;
if (m_head == n)
{
m_head = static_cast<row *>(m_head->m_next);
if (m_head != nullptr)
m_head->m_prev = nullptr;
else
m_tail = nullptr;
n->m_next = n->m_prev = n->m_parent = nullptr;
delete_row(n);
cur = m_head; iterator erase_impl(const_iterator pos);
}
else
{
cur = static_cast<row *>(n->m_next);
if (m_tail == n)
m_tail = static_cast<row *>(n->m_prev);
row *p = m_head;
while (p != nullptr and p->m_next != n)
p = p->m_next;
if (p != nullptr and p->m_next == n)
{
p->m_next = n->m_next;
if (p->m_next != nullptr)
p->m_next->m_prev = p;
n->m_next = nullptr;
}
else
throw std::runtime_error("remove for a row not found in the list");
delete_row(n);
}
return iterator(*this, cur);
}
allocator_type m_allocator;
std::string m_name; std::string m_name;
std::vector<item_column, typename std::allocator_traits<allocator_type>::template rebind_alloc<item_column>> m_columns; std::vector<item_column> m_columns;
row *m_head = nullptr, *m_tail = nullptr; row *m_head = nullptr, *m_tail = nullptr;
}; };
using category = category_t<>;
} // namespace cif::v2 } // namespace cif::v2
\ No newline at end of file
...@@ -41,145 +41,120 @@ namespace cif::v2 ...@@ -41,145 +41,120 @@ namespace cif::v2
namespace detail namespace detail
{ {
template <typename Category>
struct condition_impl struct condition_impl
{ {
using category_type = Category;
using row_type = row_handle<category_type>;
virtual ~condition_impl() {} virtual ~condition_impl() {}
virtual void prepare(const Category &c) {} virtual void prepare(const category &c) {}
virtual bool test(const Category &c, const row_type &r) const = 0; virtual bool test(row_handle r) const = 0;
virtual void str(std::ostream &os) const = 0; virtual void str(std::ostream &os) const = 0;
}; };
template <typename Category> struct all_condition_impl : public condition_impl
struct all_condition_impl : public condition_impl<Category>
{ {
using base_type = condition_impl<Category>; bool test(row_handle r) const override { return true; }
using row_type = base_type::row_type; void str(std::ostream &os) const override { os << "*"; }
virtual bool test(const Category &c, const row_type &r) const { return true; }
virtual void str(std::ostream &os) const { os << "*"; }
}; };
template <typename>
struct or_condition_impl; struct or_condition_impl;
template <typename>
struct and_condition_impl; struct and_condition_impl;
template <typename>
struct not_condition_impl; struct not_condition_impl;
} // namespace detail } // namespace detail
template <typename Category> class condition
class condition_t
{ {
public: public:
using category_type = Category; using condition_impl = detail::condition_impl;
using condition_impl = detail::condition_impl<category_type>;
using row_type = row_handle<category_type>;
condition_t() condition()
: m_impl(nullptr) : m_impl(nullptr)
{ {
} }
condition_t(condition_impl *impl) condition(condition_impl *impl)
: m_impl(impl) : m_impl(impl)
{ {
} }
condition_t(const condition_t &) = delete; condition(const condition &) = delete;
condition_t(condition_t &&rhs) noexcept condition(condition &&rhs) noexcept
: m_impl(nullptr) : m_impl(nullptr)
{ {
std::swap(m_impl, rhs.m_impl); std::swap(m_impl, rhs.m_impl);
} }
condition_t &operator=(const condition_t &) = delete; condition &operator=(const condition &) = delete;
condition_t &operator=(condition_t &&rhs) noexcept condition &operator=(condition &&rhs) noexcept
{ {
std::swap(m_impl, rhs.m_impl); std::swap(m_impl, rhs.m_impl);
return *this; return *this;
} }
~condition_t() ~condition()
{ {
delete m_impl; delete m_impl;
m_impl = nullptr; m_impl = nullptr;
} }
void prepare(const category_type &c) void prepare(const category &c)
{ {
if (m_impl) if (m_impl)
m_impl->prepare(c); m_impl->prepare(c);
m_prepared = true; m_prepared = true;
} }
bool operator()(const category_type &c, const row_type &r) const bool operator()(row_handle r) const
{ {
assert(this->m_impl != nullptr); assert(this->m_impl != nullptr);
assert(this->m_prepared); assert(this->m_prepared);
return m_impl ? m_impl->test(c, r) : false; return m_impl ? m_impl->test(r) : false;
} }
bool empty() const { return m_impl == nullptr; } bool empty() const { return m_impl == nullptr; }
template<typename C> friend condition_t operator||(condition_t<C> &&a, condition_t<C> &&b); friend condition operator||(condition &&a, condition &&b);
template<typename C> friend condition_t operator&&(condition_t<C> &&a, condition_t<C> &&b); friend condition operator&&(condition &&a, condition &&b);
template <typename>
friend struct detail::or_condition_impl; friend struct detail::or_condition_impl;
template <typename>
friend struct detail::and_condition_impl; friend struct detail::and_condition_impl;
template <typename>
friend struct detail::not_condition_impl; friend struct detail::not_condition_impl;
void swap(condition_t &rhs) void swap(condition &rhs)
{ {
std::swap(m_impl, rhs.m_impl); std::swap(m_impl, rhs.m_impl);
std::swap(m_prepared, rhs.m_prepared); std::swap(m_prepared, rhs.m_prepared);
} }
template<typename C> friend std::ostream &operator<<(std::ostream &os, const condition_t<C> &cond); friend std::ostream &operator<<(std::ostream &os, const condition &cond)
{
if (cond.m_impl)
cond.m_impl->str(os);
return os;
}
private: private:
condition_impl *m_impl; condition_impl *m_impl;
bool m_prepared = false; bool m_prepared = false;
}; };
template <typename Category>
inline std::ostream &operator<<(std::ostream &os, const condition_t<Category> &cond)
{
if (cond.m_impl)
cond.m_impl->str(os);
return os;
}
namespace detail namespace detail
{ {
struct key_is_empty_condition_impl : public condition_impl
template <typename Category>
struct keyIsemptycondition_impl : public condition_impl<Category>
{ {
using base_type = condition_impl<Category>; key_is_empty_condition_impl(const std::string &item_tag)
using row_type = base_type::row_type;
keyIsemptycondition_impl(const std::string &item_tag)
: m_item_tag(item_tag) : m_item_tag(item_tag)
{ {
} }
virtual void prepare(const Category &c); void prepare(const category &c) override;
virtual bool test(const Category &c, const row_type &r) const bool test(row_handle r) const override
{ {
return r[m_item_ix].empty(); return r[m_item_ix].empty();
} }
virtual void str(std::ostream &os) const void str(std::ostream &os) const override
{ {
os << m_item_tag << " IS NULL"; os << m_item_tag << " IS NULL";
} }
...@@ -188,61 +163,53 @@ namespace detail ...@@ -188,61 +163,53 @@ namespace detail
size_t m_item_ix = 0; size_t m_item_ix = 0;
}; };
template <typename Category> struct key_compare_condition_impl : public condition_impl
struct keyComparecondition_impl : public condition_impl<Category>
{ {
using base_type = condition_impl<Category>;
using row_type = base_type::row_type;
template <typename COMP> template <typename COMP>
keyComparecondition_impl(const std::string &item_tag, COMP &&comp, const std::string &s) key_compare_condition_impl(const std::string &item_tag, COMP &&comp, const std::string &s)
: m_item_tag(item_tag) : m_item_tag(item_tag)
, m_compare(std::move(comp)) , m_compare(std::move(comp))
, mStr(s) , m_str(s)
{ {
} }
virtual void prepare(const Category &c); void prepare(const category &c) override;
virtual bool test(const Category &c, const row_type &r) const bool test(row_handle r) const override
{ {
return m_compare(c, r, m_icase); return m_compare(r, m_icase);
} }
virtual void str(std::ostream &os) const void str(std::ostream &os) const override
{ {
os << m_item_tag << (m_icase ? "^ " : " ") << mStr; os << m_item_tag << (m_icase ? "^ " : " ") << m_str;
} }
std::string m_item_tag; std::string m_item_tag;
size_t m_item_ix = 0; size_t m_item_ix = 0;
bool m_icase = false; bool m_icase = false;
std::function<bool(const Category &, const row_type &, bool)> m_compare; std::function<bool(row_handle, bool)> m_compare;
std::string mStr; std::string m_str;
}; };
template <typename Category> struct key_matches_condition_impl : public condition_impl
struct keyMatchescondition_impl : public condition_impl<Category>
{ {
using base_type = condition_impl<Category>; key_matches_condition_impl(const std::string &item_tag, const std::regex &rx)
using row_type = base_type::row_type;
keyMatchescondition_impl(const std::string &item_tag, const std::regex &rx)
: m_item_tag(item_tag) : m_item_tag(item_tag)
, m_item_ix(0) , m_item_ix(0)
, mRx(rx) , mRx(rx)
{ {
} }
virtual void prepare(const Category &c); void prepare(const category &c) override;
virtual bool test(const Category &c, const row_type &r) const bool test(row_handle r) const override
{ {
std::string_view txt = r[m_item_ix].text(); std::string_view txt = r[m_item_ix].text();
return std::regex_match(txt.begin(), txt.end(), mRx); return std::regex_match(txt.begin(), txt.end(), mRx);
} }
virtual void str(std::ostream &os) const void str(std::ostream &os) const override
{ {
os << m_item_tag << " =~ expression"; os << m_item_tag << " =~ expression";
} }
...@@ -252,21 +219,40 @@ namespace detail ...@@ -252,21 +219,40 @@ namespace detail
std::regex mRx; std::regex mRx;
}; };
template <typename Category, typename T> template <typename T>
struct AnyIscondition_impl : public condition_impl<Category> struct any_is_condition_impl : public condition_impl
{ {
using base_type = condition_impl<Category>;
using row_type = base_type::row_type;
typedef T valueType; typedef T valueType;
AnyIscondition_impl(const valueType &value) any_is_condition_impl(const valueType &value)
: mValue(value) : mValue(value)
{ {
} }
virtual bool test(const Category &c, const row_type &r) const; bool test(row_handle r) const override
virtual void str(std::ostream &os) const {
auto &c = r.cat();
bool result = false;
for (auto &f : get_category_fields(c))
{
try
{
if (r[f].compare(mValue) == 0)
{
result = true;
break;
}
}
catch (...)
{
}
}
return result;
}
void str(std::ostream &os) const override
{ {
os << "<any> == " << mValue; os << "<any> == " << mValue;
} }
...@@ -274,19 +260,38 @@ namespace detail ...@@ -274,19 +260,38 @@ namespace detail
valueType mValue; valueType mValue;
}; };
template <typename Category> struct any_matches_condition_impl : public condition_impl
struct AnyMatchescondition_impl : public condition_impl<Category>
{ {
using base_type = condition_impl<Category>; any_matches_condition_impl(const std::regex &rx)
using row_type = base_type::row_type;
AnyMatchescondition_impl(const std::regex &rx)
: mRx(rx) : mRx(rx)
{ {
} }
virtual bool test(const Category &c, const row_type &r) const; bool test(row_handle r) const override
virtual void str(std::ostream &os) const {
auto &c = r.cat();
bool result = false;
for (auto &f : get_category_fields(c))
{
try
{
std::string_view txt = r[f].text();
if (std::regex_match(txt.begin(), txt.end(), mRx))
{
result = true;
break;
}
}
catch (...)
{
}
}
return result;
}
void str(std::ostream &os) const override
{ {
os << "<any> =~ expression"; os << "<any> =~ expression";
} }
...@@ -294,13 +299,9 @@ namespace detail ...@@ -294,13 +299,9 @@ namespace detail
std::regex mRx; std::regex mRx;
}; };
template <typename Category> struct and_condition_impl : public condition_impl
struct and_condition_impl : public condition_impl<Category>
{ {
using base_type = condition_impl<Category>; and_condition_impl(condition &&a, condition &&b)
using row_type = base_type::row_type;
and_condition_impl(condition_t<Category> &&a, condition_t<Category> &&b)
: mA(nullptr) : mA(nullptr)
, mB(nullptr) , mB(nullptr)
{ {
...@@ -314,18 +315,18 @@ namespace detail ...@@ -314,18 +315,18 @@ namespace detail
delete mB; delete mB;
} }
virtual void prepare(const Category &c) void prepare(const category &c) override
{ {
mA->prepare(c); mA->prepare(c);
mB->prepare(c); mB->prepare(c);
} }
virtual bool test(const Category &c, const row_type &r) const bool test(row_handle r) const override
{ {
return mA->test(c, r) and mB->test(c, r); return mA->test(r) and mB->test(r);
} }
virtual void str(std::ostream &os) const void str(std::ostream &os) const override
{ {
os << '('; os << '(';
mA->str(os); mA->str(os);
...@@ -334,17 +335,13 @@ namespace detail ...@@ -334,17 +335,13 @@ namespace detail
os << ')'; os << ')';
} }
base_type *mA; condition_impl *mA;
base_type *mB; condition_impl *mB;
}; };
template <typename Category> struct or_condition_impl : public condition_impl
struct or_condition_impl : public condition_impl<Category>
{ {
using base_type = condition_impl<Category>; or_condition_impl(condition &&a, condition &&b)
using row_type = base_type::row_type;
or_condition_impl(condition_t<Category> &&a, condition_t<Category> &&b)
: mA(nullptr) : mA(nullptr)
, mB(nullptr) , mB(nullptr)
{ {
...@@ -358,18 +355,18 @@ namespace detail ...@@ -358,18 +355,18 @@ namespace detail
delete mB; delete mB;
} }
virtual void prepare(const Category &c) void prepare(const category &c) override
{ {
mA->prepare(c); mA->prepare(c);
mB->prepare(c); mB->prepare(c);
} }
virtual bool test(const Category &c, const row_type &r) const bool test(row_handle r) const override
{ {
return mA->test(c, r) or mB->test(c, r); return mA->test(r) or mB->test(r);
} }
virtual void str(std::ostream &os) const void str(std::ostream &os) const override
{ {
os << '('; os << '(';
mA->str(os); mA->str(os);
...@@ -378,17 +375,13 @@ namespace detail ...@@ -378,17 +375,13 @@ namespace detail
os << ')'; os << ')';
} }
base_type *mA; condition_impl *mA;
base_type *mB; condition_impl *mB;
}; };
template <typename Category> struct not_condition_impl : public condition_impl
struct not_condition_impl : public condition_impl<Category>
{ {
using base_type = condition_impl<Category>; not_condition_impl(condition &&a)
using row_type = base_type::row_type;
not_condition_impl(condition_t<Category> &&a)
: mA(nullptr) : mA(nullptr)
{ {
std::swap(mA, a.m_impl); std::swap(mA, a.m_impl);
...@@ -399,55 +392,53 @@ namespace detail ...@@ -399,55 +392,53 @@ namespace detail
delete mA; delete mA;
} }
virtual void prepare(const Category &c) void prepare(const category &c) override
{ {
mA->prepare(c); mA->prepare(c);
} }
virtual bool test(const Category &c, const row_type &r) const bool test(row_handle r) const override
{ {
return not mA->test(c, r); return not mA->test(r);
} }
virtual void str(std::ostream &os) const void str(std::ostream &os) const override
{ {
os << "NOT ("; os << "NOT (";
mA->str(os); mA->str(os);
os << ')'; os << ')';
} }
base_type *mA; condition_impl *mA;
}; };
} // namespace detail } // namespace detail
template <typename Category> inline condition operator&&(condition &&a, condition &&b)
inline condition_t<Category> operator&&(condition_t<Category> &&a, condition_t<Category> &&b)
{ {
if (a.m_impl and b.m_impl) if (a.m_impl and b.m_impl)
return condition_t<Category>(new detail::and_condition_impl<Category>(std::move(a), std::move(b))); return condition(new detail::and_condition_impl(std::move(a), std::move(b)));
if (a.m_impl) if (a.m_impl)
return condition_t<Category>(std::move(a)); return condition(std::move(a));
return condition_t<Category>(std::move(b)); return condition(std::move(b));
} }
template <typename Category> inline condition operator||(condition &&a, condition &&b)
inline condition_t<Category> operator||(condition_t<Category> &&a, condition_t<Category> &&b)
{ {
if (a.m_impl and b.m_impl) if (a.m_impl and b.m_impl)
return condition_t<Category>(new detail::or_condition_impl<Category>(std::move(a), std::move(b))); return condition(new detail::or_condition_impl(std::move(a), std::move(b)));
if (a.m_impl) if (a.m_impl)
return condition_t<Category>(std::move(a)); return condition(std::move(a));
return condition_t<Category>(std::move(b)); return condition(std::move(b));
} }
struct empty struct empty_type
{ {
}; };
/// \brief A helper to make it possible to have conditions like ("id"_key == cif::null) /// \brief A helper to make it possible to have conditions like ("id"_key == cif::null)
inline constexpr empty null = empty(); inline constexpr empty_type null = empty_type();
struct key struct key
{ {
...@@ -467,161 +458,138 @@ struct key ...@@ -467,161 +458,138 @@ struct key
std::string m_item_tag; std::string m_item_tag;
}; };
template <typename Category, typename T> template <typename T>
condition_t<Category> operator==(const key &key, const T &v) condition operator==(const key &key, const T &v)
{ {
using category_type = Category;
using row_type = row_handle<category_type>;
std::ostringstream s; std::ostringstream s;
s << " == " << v; s << " == " << v;
return condition_t<Category>(new detail::keyComparecondition_impl<Category>( return condition(new detail::key_compare_condition_impl(
key.m_item_tag, [tag = key.m_item_tag, v](const Category &c, const row_type &r, bool icase) key.m_item_tag, [tag = key.m_item_tag, v](row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) == 0; }, { return r[tag].template compare<T>(v, icase) == 0; },
s.str())); s.str()));
} }
template <typename Category> inline condition operator==(const key &key, const char *value)
inline condition_t<Category> operator==(const key &key, const char *value)
{ {
using category_type = Category;
using row_type = row_handle<category_type>;
if (value != nullptr and *value != 0) if (value != nullptr and *value != 0)
{ {
std::ostringstream s; std::ostringstream s;
s << " == " << value; s << " == " << value;
return condition_t<Category>(new detail::keyComparecondition_impl<Category>( return condition(new detail::key_compare_condition_impl(
key.m_item_tag, [tag = key.m_item_tag, value](const Category &c, const row_type &r, bool icase) key.m_item_tag, [tag = key.m_item_tag, value](row_handle r, bool icase)
{ return r[tag].compare(value, icase) == 0; }, { return r[tag].compare(value, icase) == 0; },
s.str())); s.str()));
} }
else else
return condition_t(new detail::keyIsemptycondition_impl<Category>(key.m_item_tag)); return condition(new detail::key_is_empty_condition_impl(key.m_item_tag));
} }
// inline condition_t operator==(const key& key, const detail::ItemReference& v) // inline condition_t operator==(const key& key, const detail::ItemReference& v)
// { // {
// if (v.empty()) // if (v.empty())
// return condition_t(new detail::keyIsemptycondition_impl(key.m_item_tag)); // return condition_t(new detail::key_is_empty_condition_impl(key.m_item_tag));
// else // else
// return condition_t(new detail::keyComparecondition_impl(key.m_item_tag, [tag = key.m_item_tag, v](const Category& c, const row_type& r, bool icase) // return condition_t(new detail::key_compare_condition_impl(key.m_item_tag, [tag = key.m_item_tag, v](const category& c, const row& r, bool icase)
// { return r[tag].template compare<(v, icase) == 0; })); // { return r[tag].template compare<(v, icase) == 0; }));
// } // }
template <typename Category, typename T> template <typename T>
condition_t<Category> operator!=(const key &key, const T &v) condition operator!=(const key &key, const T &v)
{ {
return condition_t<Category>(new detail::not_condition_impl<Category>(operator==(key, v))); return condition(new detail::not_condition_impl(operator==(key, v)));
} }
template <typename Category> inline condition operator!=(const key &key, const char *v)
inline condition_t<Category> operator!=(const key &key, const char *v)
{ {
std::string value(v ? v : ""); std::string value(v ? v : "");
return condition_t<Category>(new detail::not_condition_impl<Category>(operator==(key, value))); return condition(new detail::not_condition_impl(operator==(key, value)));
} }
template <typename Category, typename T> template <typename T>
condition_t<Category> operator>(const key &key, const T &v) condition operator>(const key &key, const T &v)
{ {
using category_type = Category;
using row_type = row_handle<category_type>;
std::ostringstream s; std::ostringstream s;
s << " > " << v; s << " > " << v;
return condition_t<Category>(new detail::keyComparecondition_impl<Category>( return condition(new detail::key_compare_condition_impl(
key.m_item_tag, [tag = key.m_item_tag, v](const Category &c, const row_type &r, bool icase) key.m_item_tag, [tag = key.m_item_tag, v](const category &c, row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) > 0; }, { return r[tag].template compare<T>(v, icase) > 0; },
s.str())); s.str()));
} }
template <typename Category, typename T> template <typename T>
condition_t<Category> operator>=(const key &key, const T &v) condition operator>=(const key &key, const T &v)
{ {
using category_type = Category;
using row_type = row_handle<category_type>;
std::ostringstream s; std::ostringstream s;
s << " >= " << v; s << " >= " << v;
return condition_t<Category>(new detail::keyComparecondition_impl<Category>( return condition(new detail::key_compare_condition_impl(
key.m_item_tag, [tag = key.m_item_tag, v](const Category &c, const row_type &r, bool icase) key.m_item_tag, [tag = key.m_item_tag, v](const category &c, row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) >= 0; }, { return r[tag].template compare<T>(v, icase) >= 0; },
s.str())); s.str()));
} }
template <typename Category, typename T> template <typename T>
condition_t<Category> operator<(const key &key, const T &v) condition operator<(const key &key, const T &v)
{ {
using category_type = Category;
using row_type = row_handle<category_type>;
std::ostringstream s; std::ostringstream s;
s << " < " << v; s << " < " << v;
return condition_t<Category>(new detail::keyComparecondition_impl<Category>( return condition(new detail::key_compare_condition_impl(
key.m_item_tag, [tag = key.m_item_tag, v](const Category &c, const row_type &r, bool icase) key.m_item_tag, [tag = key.m_item_tag, v](const category &c, row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) < 0; }, { return r[tag].template compare<T>(v, icase) < 0; },
s.str())); s.str()));
} }
template <typename Category, typename T> template <typename T>
condition_t<Category> operator<=(const key &key, const T &v) condition operator<=(const key &key, const T &v)
{ {
using category_type = Category;
using row_type = row_handle<category_type>;
std::ostringstream s; std::ostringstream s;
s << " <= " << v; s << " <= " << v;
return condition_t<Category>(new detail::keyComparecondition_impl<Category>( return condition(new detail::key_compare_condition_impl(
key.m_item_tag, [tag = key.m_item_tag, v](const Category &c, const row_type &r, bool icase) key.m_item_tag, [tag = key.m_item_tag, v](const category &c, row_handle r, bool icase)
{ return r[tag].template compare<T>(v, icase) <= 0; }, { return r[tag].template compare<T>(v, icase) <= 0; },
s.str())); s.str()));
} }
template <typename Category> inline condition operator==(const key &key, const std::regex &rx)
inline condition_t<Category> operator==(const key &key, const std::regex &rx)
{ {
return condition_t<Category>(new detail::keyMatchescondition_impl<Category>(key.m_item_tag, rx)); return condition(new detail::key_matches_condition_impl(key.m_item_tag, rx));
} }
template <typename Category> inline condition operator==(const key &key, const empty_type &)
inline condition_t<Category> operator==(const key &key, const empty &)
{ {
return condition_t<Category>(new detail::keyIsemptycondition_impl<Category>(key.m_item_tag)); return condition(new detail::key_is_empty_condition_impl(key.m_item_tag));
} }
template <typename Category> struct any_type
struct any_t
{ {
}; };
template <typename Category, typename T> inline constexpr any_type any = any_type{};
condition_t<Category> operator==(const any_t<Category> &, const T &v)
template <typename T>
condition operator==(const any_type &, const T &v)
{ {
return condition_t<Category>(new detail::AnyIscondition_impl<Category, T>(v)); return condition(new detail::any_is_condition_impl<T>(v));
} }
template <typename Category> inline condition operator==(const any_type &, const std::regex &rx)
condition_t<Category> operator==(any_t<Category> &, const std::regex &rx)
{ {
return condition_t<Category>(new detail::AnyMatchescondition_impl<Category>(rx)); return condition(new detail::any_matches_condition_impl(rx));
} }
template <typename Category> inline condition all()
inline condition_t<Category> all()
{ {
return condition_t<Category>(new detail::all_condition_impl<Category>()); return condition(new detail::all_condition_impl());
} }
// inline condition_t<Category> Not(condition_t<Category> &&cond) // inline condition_t<category> Not(condition_t<category> &&cond)
// { // {
// return condition_t<Category>(new detail::not_condition_impl<Category>(std::move(cond))); // return condition_t<category>(new detail::not_condition_impl(std::move(cond)));
// } // }
namespace literals namespace literals
...@@ -630,9 +598,6 @@ namespace literals ...@@ -630,9 +598,6 @@ namespace literals
{ {
return key(std::string(text, length)); return key(std::string(text, length));
} }
inline constexpr empty null = empty();
} // namespace literals } // namespace literals
} // namespace cif::v2 } // namespace cif::v2
\ No newline at end of file
...@@ -35,47 +35,29 @@ namespace cif::v2 ...@@ -35,47 +35,29 @@ namespace cif::v2
// -------------------------------------------------------------------- // --------------------------------------------------------------------
template <typename Alloc, typename Category> class datablock
class datablock_t
{ {
public: public:
using category_type = Category; using category_list = std::list<category>;
using allocator_type = Alloc;
using category_allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<category_type>; using iterator = category_list::iterator;
using category_type_list = std::list<category_type, category_allocator_type>; using const_iterator = category_list::const_iterator;
using iterator = category_type_list::iterator; using reference = typename category_list::reference;
using const_iterator = category_type_list::const_iterator;
using reference = typename category_type_list::reference; datablock() = default;
datablock_t(std::string_view name, const allocator_type &alloc = allocator_type()) datablock(std::string_view name)
: m_categories(alloc) : m_name(name)
, m_name(name)
{ {
} }
datablock_t(const datablock_t &) = default; datablock(const datablock &) = default;
datablock_t(datablock_t &&) = default; datablock(datablock &&) = default;
// template <typename Alloc2> datablock &operator=(const datablock &) = default;
// datablock_t(const datablock_t &db, const Alloc2 &a) datablock &operator=(datablock &&) = default;
// : m_categories(db, a)
// , m_name(db.m_name)
// {
// }
// template <typename Alloc2>
// datablock_t(datablock_t &&db, const Alloc2 &a)
// : base_type(std::move(db), a)
// , m_name(db.m_name)
// {
// }
datablock_t &operator=(const datablock_t &) = default;
datablock_t &operator=(datablock_t &&) = default;
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -100,9 +82,9 @@ class datablock_t ...@@ -100,9 +82,9 @@ class datablock_t
// -------------------------------------------------------------------- // --------------------------------------------------------------------
category_type &operator[](std::string_view name) category &operator[](std::string_view name)
{ {
auto i = std::find_if(m_categories.begin(), m_categories.end(), [name](const category_type &c) auto i = std::find_if(m_categories.begin(), m_categories.end(), [name](const category &c)
{ return iequals(c.name(), name); }); { return iequals(c.name(), name); });
if (i != m_categories.end()) if (i != m_categories.end())
...@@ -112,10 +94,10 @@ class datablock_t ...@@ -112,10 +94,10 @@ class datablock_t
return m_categories.back(); return m_categories.back();
} }
const category_type &operator[](std::string_view name) const const category &operator[](std::string_view name) const
{ {
static const category_type s_empty; static const category s_empty;
auto i = std::find_if(m_categories.begin(), m_categories.end(), [name](const category_type &c) auto i = std::find_if(m_categories.begin(), m_categories.end(), [name](const category &c)
{ return iequals(c.name(), name); }); { return iequals(c.name(), name); });
return i == m_categories.end() ? s_empty : *i; return i == m_categories.end() ? s_empty : *i;
} }
...@@ -155,53 +137,51 @@ class datablock_t ...@@ -155,53 +137,51 @@ class datablock_t
return std::make_tuple(m_categories.begin(), is_new); return std::make_tuple(m_categories.begin(), is_new);
} }
void write(std::ostream &os) const // void write(std::ostream &os) const
{ // {
// std::shared_lock lock(mLock); // // std::shared_lock lock(mLock);
os << "data_" << m_name << std::endl // os << "data_" << m_name << std::endl
<< "# " << std::endl; // << "# " << std::endl;
// mmcif support, sort of. First write the 'entry' Category // // mmcif support, sort of. First write the 'entry' Category
// and if it exists, _AND_ we have a Validator, write out the // // and if it exists, _AND_ we have a Validator, write out the
// audit_conform record. // // audit_conform record.
for (auto &cat : m_categories) // for (auto &cat : m_categories)
{ // {
if (cat.name() != "entry") // if (cat.name() != "entry")
continue; // continue;
cat.write(os); // cat.write(os);
// if (mValidator != nullptr) // // if (mValidator != nullptr)
// { // // {
// Category auditConform(*this, "audit_conform", nullptr); // // Category auditConform(*this, "audit_conform", nullptr);
// auditConform.emplace({{"dict_name", mValidator->dictName()}, // // auditConform.emplace({{"dict_name", mValidator->dictName()},
// {"dict_version", mValidator->dictVersion()}}); // // {"dict_version", mValidator->dictVersion()}});
// auditConform.write(os); // // auditConform.write(os);
// } // // }
break; // break;
} // }
for (auto &cat : m_categories) // for (auto &cat : m_categories)
{ // {
if (cat.name() != "entry" and cat.name() != "audit_conform") // if (cat.name() != "entry" and cat.name() != "audit_conform")
cat.write(os); // cat.write(os);
} // }
} // }
friend std::ostream &operator<<(std::ostream &os, const datablock_t &db) // friend std::ostream &operator<<(std::ostream &os, const datablock &db)
{ // {
db.write(os); // db.write(os);
return os; // return os;
} // }
private: private:
category_type_list m_categories; category_list m_categories;
std::string m_name; std::string m_name;
}; };
using datablock = datablock_t<>;
} // namespace cif::v2 } // namespace cif::v2
\ No newline at end of file
...@@ -36,20 +36,10 @@ namespace cif::v2 ...@@ -36,20 +36,10 @@ namespace cif::v2
// -------------------------------------------------------------------- // --------------------------------------------------------------------
template < class file
typename Alloc,
typename Datablock,
typename Category>
class file_t
{ {
public: public:
using allocator_type = Alloc; using datablock_list = std::list<datablock>;
using datablock_type = Datablock;
using category_type = typename datablock_type::category_type;
using datablock_allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<datablock_type>;
using datablock_list = std::list<datablock_type, datablock_allocator_type>;
using value_type = datablock_list::value_type; using value_type = datablock_list::value_type;
using reference = datablock_list::reference; using reference = datablock_list::reference;
...@@ -58,29 +48,21 @@ class file_t ...@@ -58,29 +48,21 @@ class file_t
using iterator = datablock_list::iterator; using iterator = datablock_list::iterator;
using const_iterator = datablock_list::const_iterator; using const_iterator = datablock_list::const_iterator;
using parser_type = parser_t<allocator_type, file_t, datablock_type, category_type>; file() = default;
file_t() = default;
file_t(const allocator_type &a = allocator_type()) file(std::istream &is)
: m_datablocks(a)
{
}
file_t(std::istream &is, const allocator_type &alloc = allocator_type())
: m_datablocks(alloc)
{ {
load(is); load(is);
} }
file_t(const file_t &) = default; file(const file &) = default;
file_t(file_t &&) = default; file(file &&) = default;
file_t &operator=(const file_t &) = default; file &operator=(const file &) = default;
file_t &operator=(file_t &&) = default; file &operator=(file &&) = default;
datablock_type &operator[](std::string_view name) datablock &operator[](std::string_view name)
{ {
auto i = std::find_if(m_datablocks.begin(), m_datablocks.end(), [name](const datablock_type &c) auto i = std::find_if(m_datablocks.begin(), m_datablocks.end(), [name](const datablock &c)
{ return iequals(c.name(), name); }); { return iequals(c.name(), name); });
if (i != m_datablocks.end()) if (i != m_datablocks.end())
...@@ -90,10 +72,10 @@ class file_t ...@@ -90,10 +72,10 @@ class file_t
return m_datablocks.back(); return m_datablocks.back();
} }
const datablock_type &operator[](std::string_view name) const const datablock &operator[](std::string_view name) const
{ {
static const datablock_type s_empty; static const datablock s_empty;
auto i = std::find_if(m_datablocks.begin(), m_datablocks.end(), [name](const datablock_type &c) auto i = std::find_if(m_datablocks.begin(), m_datablocks.end(), [name](const datablock &c)
{ return iequals(c.name(), name); }); { return iequals(c.name(), name); });
return i == m_datablocks.end() ? s_empty : *i; return i == m_datablocks.end() ? s_empty : *i;
} }
...@@ -133,14 +115,12 @@ class file_t ...@@ -133,14 +115,12 @@ class file_t
reference front() { return m_datablocks.front(); } reference front() { return m_datablocks.front(); }
reference back() { return m_datablocks.back(); } reference back() { return m_datablocks.back(); }
void load(std::istream &is) void load(std::istream &is)
{ {
// auto saved = mValidator; // auto saved = mValidator;
// setValidator(nullptr); // setValidator(nullptr);
parser_type p(is, *this); parser p(is, *this);
p.parse_file(); p.parse_file();
// if (saved != nullptr) // if (saved != nullptr)
...@@ -152,8 +132,7 @@ class file_t ...@@ -152,8 +132,7 @@ class file_t
private: private:
datablock_list m_datablocks; datablock_list m_datablocks;
std::unique_ptr<Validator> m_validator;
}; };
using file = file_t<>;
} }
\ No newline at end of file
...@@ -26,21 +26,27 @@ ...@@ -26,21 +26,27 @@
#pragma once #pragma once
#include <memory> #include <string>
#include <vector>
namespace cif::v2 namespace cif::v2
{ {
template <typename Alloc = std::allocator<void>> class category;
class category_t; class datablock;
class file;
class parser;
template <typename Alloc = std::allocator<void>, typename Category = category_t<Alloc>> class row;
class datablock_t; class row_handle;
template <typename Alloc = std::allocator<void>, typename Datablock = datablock_t<Alloc>, typename Category = typename Datablock::category_type> class item;
class file_t; class item_handle;
// --------------------------------------------------------------------
// let's make life easier
std::vector<std::string> get_category_fields(const category &cat);
template <typename Alloc = std::allocator<void>, typename File = file_t<Alloc>, typename Datablock = datablock_t<Alloc>, typename Category = typename Datablock::category_type>
class parser_t;
} // namespace cif::v2 } // namespace cif::v2
\ No newline at end of file
...@@ -29,12 +29,15 @@ ...@@ -29,12 +29,15 @@
#include <charconv> #include <charconv>
#include <cstring> #include <cstring>
#include <iomanip> #include <iomanip>
#include <iostream>
#include <limits> #include <limits>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <cif++/CifUtils.hpp> #include <cif++/CifUtils.hpp>
#include <cif++/v2/forward_decl.hpp>
namespace cif namespace cif
{ {
extern int VERBOSE; extern int VERBOSE;
...@@ -80,7 +83,7 @@ class item ...@@ -80,7 +83,7 @@ class item
auto r = cif::to_chars(m_buffer, m_buffer + sizeof(m_buffer) - 1, value, cif::chars_format::general); auto r = cif::to_chars(m_buffer, m_buffer + sizeof(m_buffer) - 1, value, cif::chars_format::general);
if (r.ec != std::errc()) if (r.ec != std::errc())
throw std::runtime_error("Could not format number"); throw std::runtime_error("Could not format number");
assert(r.ptr >= m_buffer and r.ptr < m_buffer + sizeof(m_buffer)); assert(r.ptr >= m_buffer and r.ptr < m_buffer + sizeof(m_buffer));
*r.ptr = 0; *r.ptr = 0;
m_value = std::string_view(m_buffer, r.ptr - m_buffer); m_value = std::string_view(m_buffer, r.ptr - m_buffer);
...@@ -138,13 +141,55 @@ class item ...@@ -138,13 +141,55 @@ class item
}; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
/// \brief the internal storage for items in a category
///
/// Internal storage, strictly forward linked list with minimal space
/// requirements. Strings of size 7 or shorter are stored internally.
/// Typically, more than 99% of the strings in an mmCIF file are less
/// than 8 bytes in length.
struct item_value
{
item_value(uint16_t column_ix, uint16_t length)
: m_next(nullptr)
, m_column_ix(column_ix)
, m_length(length)
{
}
item_value() = delete;
item_value(const item_value &) = delete;
item_value &operator=(const item_value &) = delete;
item_value *m_next;
uint16_t m_column_ix;
uint16_t m_length;
union
{
char m_local_data[8];
char *m_data;
};
static constexpr size_t kBufferSize = sizeof(m_local_data);
std::string_view text() const
{
return {m_length >= kBufferSize ? m_data : m_local_data, m_length};
}
const char *c_str() const
{
return m_length >= kBufferSize ? m_data : m_local_data;
}
};
static_assert(sizeof(item_value) == 24, "sizeof(item_value) should be 24 bytes");
// --------------------------------------------------------------------
// Transient object to access stored data // Transient object to access stored data
template <typename RowHandle>
struct item_handle struct item_handle
{ {
using row_handle_type = RowHandle;
public: public:
// conversion helper class // conversion helper class
template <typename T, typename = void> template <typename T, typename = void>
...@@ -158,29 +203,6 @@ struct item_handle ...@@ -158,29 +203,6 @@ struct item_handle
return *this; return *this;
} }
// template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
// item_handle &operator=(const T &value)
// {
// this->operator=(std::to_string(value));
// return *this;
// }
// template <typename T>
// item_handle &operator=(const std::optional<T> &value)
// {
// if (value)
// this->operator=(*value);
// else
// this->operator=("?");
// return *this;
// }
// item_handle &operator=(std::string_view value)
// {
// m_row_handle.assign(m_column, value, false);
// return *this;
// }
template <typename... Ts> template <typename... Ts>
void os(const Ts &...v) void os(const Ts &...v)
{ {
...@@ -199,7 +221,7 @@ struct item_handle ...@@ -199,7 +221,7 @@ struct item_handle
} }
template <typename T> template <typename T>
auto value_or(const T &dv) auto value_or(const T &dv) const
{ {
return empty() ? dv : this->as<T>(); return empty() ? dv : this->as<T>();
} }
...@@ -244,21 +266,12 @@ struct item_handle ...@@ -244,21 +266,12 @@ struct item_handle
// return s_empty_result; // return s_empty_result;
// } // }
std::string_view text() const std::string_view text() const;
{
for (auto iv = m_row_handle.m_row->m_head; iv != nullptr; iv = iv->m_next)
{
if (iv->m_column_ix == m_column)
return iv->text();
}
return {};
}
// bool operator!=(const std::string &s) const { return s != c_str(); } // bool operator!=(const std::string &s) const { return s != c_str(); }
// bool operator==(const std::string &s) const { return s == c_str(); } // bool operator==(const std::string &s) const { return s == c_str(); }
item_handle(uint16_t column, row_handle_type &row) item_handle(uint16_t column, row_handle &row)
: m_column(column) : m_column(column)
, m_row_handle(row) , m_row_handle(row)
{ {
...@@ -266,17 +279,15 @@ struct item_handle ...@@ -266,17 +279,15 @@ struct item_handle
private: private:
uint16_t m_column; uint16_t m_column;
row_handle_type &m_row_handle; row_handle &m_row_handle;
// bool mConst = false;
static constexpr const char *s_empty_result = ""; static constexpr const char *s_empty_result = "";
}; };
// So sad that the gcc implementation of from_chars does not support floats yet... // So sad that the gcc implementation of from_chars does not support floats yet...
template <typename Row>
template <typename T> template <typename T>
struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<T>>> struct item_handle::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<T>>>
{ {
using value_type = std::remove_reference_t<std::remove_cv_t<T>>; using value_type = std::remove_reference_t<std::remove_cv_t<T>>;
...@@ -348,9 +359,8 @@ struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_arithmetic_v< ...@@ -348,9 +359,8 @@ struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_arithmetic_v<
} }
}; };
template <typename Row>
template <typename T> template <typename T>
struct item_handle<Row>::item_value_as<std::optional<T>> struct item_handle::item_value_as<std::optional<T>>
{ {
static std::optional<T> convert(const item_handle &ref) static std::optional<T> convert(const item_handle &ref)
{ {
...@@ -374,9 +384,8 @@ struct item_handle<Row>::item_value_as<std::optional<T>> ...@@ -374,9 +384,8 @@ struct item_handle<Row>::item_value_as<std::optional<T>>
} }
}; };
template <typename Row>
template <typename T> template <typename T>
struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_same_v<T, bool>>> struct item_handle::item_value_as<T, std::enable_if_t<std::is_same_v<T, bool>>>
{ {
static bool convert(const item_handle &ref) static bool convert(const item_handle &ref)
{ {
...@@ -394,9 +403,8 @@ struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_same_v<T, boo ...@@ -394,9 +403,8 @@ struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_same_v<T, boo
} }
}; };
template <typename Row>
template <size_t N> template <size_t N>
struct item_handle<Row>::item_value_as<char[N]> struct item_handle::item_value_as<char[N]>
{ {
// static std::string_view convert(const item_handle &ref) // static std::string_view convert(const item_handle &ref)
// { // {
...@@ -409,9 +417,8 @@ struct item_handle<Row>::item_value_as<char[N]> ...@@ -409,9 +417,8 @@ struct item_handle<Row>::item_value_as<char[N]>
} }
}; };
template <typename Row>
template <typename T> template <typename T>
struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_same_v<T, const char *>>> struct item_handle::item_value_as<T, std::enable_if_t<std::is_same_v<T, const char *>>>
{ {
// static std::string_view convert(const item_handle &ref) // static std::string_view convert(const item_handle &ref)
// { // {
...@@ -424,14 +431,13 @@ struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_same_v<T, con ...@@ -424,14 +431,13 @@ struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_same_v<T, con
} }
}; };
template <typename Row>
template <typename T> template <typename T>
struct item_handle<Row>::item_value_as<T, std::enable_if_t<std::is_same_v<T, std::string>>> struct item_handle::item_value_as<T, std::enable_if_t<std::is_same_v<T, std::string>>>
{ {
static std::string convert(const item_handle &ref) static std::string convert(const item_handle &ref)
{ {
std::string_view txt = ref.text(); std::string_view txt = ref.text();
return { txt.data(), txt.size() }; return {txt.data(), txt.size()};
} }
static int compare(const item_handle &ref, const std::string &value, bool icase) static int compare(const item_handle &ref, const std::string &value, bool icase)
......
...@@ -40,26 +40,24 @@ class iterator_impl ...@@ -40,26 +40,24 @@ class iterator_impl
template <typename, typename...> template <typename, typename...>
friend class iterator_impl; friend class iterator_impl;
template <typename> friend class category;
friend class category_t;
static constexpr size_t N = sizeof...(Ts); static constexpr size_t N = sizeof...(Ts);
using category_type = Category; using category_type = std::remove_cv_t<Category>;
using row_type = std::conditional_t<std::is_const_v<category_type>, const typename category_type::row, typename category_type::row>; using row_type = std::conditional_t<std::is_const_v<Category>, const row, row>;
using row_handle_type = std::conditional_t<std::is_const_v<category_type>, const row_handle<category_type>, row_handle<category_type>>;
using iterator_category = std::forward_iterator_tag; using iterator_category = std::forward_iterator_tag;
using value_type = std::conditional_t<N == 0, row_handle_type, std::tuple<Ts...>>; using value_type = std::conditional_t<N == 0, row_handle, std::tuple<Ts...>>;
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using pointer = std::conditional_t<N == 0, row_handle_type, value_type *>; using pointer = std::conditional_t<N == 0, row_handle, value_type *>;
using reference = std::conditional_t<N == 0, row_handle_type, value_type &>; using reference = std::conditional_t<N == 0, row_handle, value_type &>;
iterator_impl() = default; iterator_impl() = default;
iterator_impl(const iterator_impl &rhs) = default; iterator_impl(const iterator_impl &rhs) = default;
template<typename C2, typename... T2s> template <typename C2, typename... T2s>
iterator_impl(const iterator_impl<C2, T2s...> &rhs) iterator_impl(const iterator_impl<C2, T2s...> &rhs)
: m_category(rhs.m_category) : m_category(rhs.m_category)
, m_current(rhs.m_current) , m_current(rhs.m_current)
...@@ -68,10 +66,10 @@ class iterator_impl ...@@ -68,10 +66,10 @@ class iterator_impl
{ {
} }
iterator_impl(category_type &cat, row_type *current) iterator_impl(Category &cat, row *current)
: m_category(&cat) : m_category(const_cast<category_type *>(&cat))
, m_current(current) , m_current(current)
, m_value(cat, *current) , m_value(*m_category, *current)
{ {
static_assert(N == 0, "Only valid if this is a row iterator, not a row<xxx> iterator"); static_assert(N == 0, "Only valid if this is a row iterator, not a row<xxx> iterator");
} }
...@@ -139,10 +137,15 @@ class iterator_impl ...@@ -139,10 +137,15 @@ class iterator_impl
return &m_value; return &m_value;
} }
row_type row() const // const row_type *get_row() const
{ // {
return m_current; // return m_current;
} // }
// row_type *get_row()
// {
// return m_current;
// }
iterator_impl &operator++() iterator_impl &operator++()
{ {
...@@ -183,7 +186,7 @@ class iterator_impl ...@@ -183,7 +186,7 @@ class iterator_impl
{ {
if (m_current != nullptr) if (m_current != nullptr)
{ {
row_handle_type rh{*m_category, *m_current}; row_handle rh{*m_category, *m_current};
return std::tuple<Ts...>{rh[m_column_ix[Is]].template as<Ts>()...}; return std::tuple<Ts...>{rh[m_column_ix[Is]].template as<Ts>()...};
} }
...@@ -206,8 +209,7 @@ class iterator_proxy ...@@ -206,8 +209,7 @@ class iterator_proxy
static constexpr const size_t N = sizeof...(Ts); static constexpr const size_t N = sizeof...(Ts);
using category_type = Category; using category_type = Category;
using row_type = std::conditional_t<std::is_const_v<category_type>, const typename category_type::row, typename category_type::row>; using row_type = std::conditional_t<std::is_const_v<category_type>, const row, row>;
using row_handle_type = std::conditional_t<std::is_const_v<category_type>, const row_handle<category_type>, row_handle<category_type>>;
using iterator = iterator_impl<category_type, Ts...>; using iterator = iterator_impl<category_type, Ts...>;
using row_iterator = iterator_impl<category_type>; using row_iterator = iterator_impl<category_type>;
...@@ -230,8 +232,8 @@ class iterator_proxy ...@@ -230,8 +232,8 @@ class iterator_proxy
size_t size() const { return std::distance(begin(), end()); } size_t size() const { return std::distance(begin(), end()); }
row_type front() { return *begin(); } // row front() { return *begin(); }
row_type back() { return *(std::prev(end())); } // row back() { return *(std::prev(end())); }
category_type &category() const { return *m_category; } category_type &category() const { return *m_category; }
...@@ -249,114 +251,116 @@ class iterator_proxy ...@@ -249,114 +251,116 @@ class iterator_proxy
std::array<size_t, N> m_column_ix; std::array<size_t, N> m_column_ix;
}; };
// // -------------------------------------------------------------------- // --------------------------------------------------------------------
// // conditional iterator proxy // conditional iterator proxy
// template <typename CategoryType, typename... Ts> template <typename CategoryType, typename... Ts>
// class conditional_iterator_proxy class conditional_iterator_proxy
// { {
// public: public:
// static constexpr const size_t N = sizeof...(Ts); static constexpr const size_t N = sizeof...(Ts);
// using base_iterator = iterator_impl<CategoryType, Ts...>; using category_type = std::remove_cv_t<CategoryType>;
// using value_type = typename base_iterator::value_type;
// using row_type = typename base_iterator::row_type;
// using row_iterator = iterator_impl<row_type>;
// class conditional_iterator_impl using base_iterator = iterator_impl<CategoryType, Ts...>;
// { using value_type = typename base_iterator::value_type;
// public: using row_type = typename base_iterator::row_type;
// using iterator_category = std::forward_iterator_tag; using row_iterator = iterator_impl<CategoryType>;
// using value_type = conditional_iterator_proxy::value_type;
// using difference_type = std::ptrdiff_t;
// using pointer = value_type *;
// using reference = value_type &;
// conditional_iterator_impl(CategoryType &cat, row_iterator pos, const Condition &cond, const std::array<size_t, N> &cix); class conditional_iterator_impl
// conditional_iterator_impl(const conditional_iterator_impl &i) = default; {
// conditional_iterator_impl &operator=(const conditional_iterator_impl &i) = default; public:
using iterator_category = std::forward_iterator_tag;
using value_type = conditional_iterator_proxy::value_type;
using difference_type = std::ptrdiff_t;
using pointer = value_type *;
using reference = row_handle;
conditional_iterator_impl(CategoryType &cat, row_iterator pos, const condition &cond, const std::array<size_t, N> &cix);
conditional_iterator_impl(const conditional_iterator_impl &i) = default;
conditional_iterator_impl &operator=(const conditional_iterator_impl &i) = default;
// virtual ~conditional_iterator_impl() = default; virtual ~conditional_iterator_impl() = default;
// reference operator*() reference operator*()
// { {
// return *mBegin; return *mBegin;
// } }
// pointer operator->() pointer operator->()
// { {
// return &*mBegin; return &*mBegin;
// } }
// conditional_iterator_impl &operator++() conditional_iterator_impl &operator++()
// { {
// while (mBegin != mEnd) while (mBegin != mEnd)
// { {
// if (++mBegin == mEnd) if (++mBegin == mEnd)
// break; break;
// if ((*mCondition)(*mCat, mBegin.row())) if ((*m_condition)(*mBegin))
// break; break;
// } }
// return *this; return *this;
// } }
// conditional_iterator_impl operator++(int) conditional_iterator_impl operator++(int)
// { {
// conditional_iterator_impl result(*this); conditional_iterator_impl result(*this);
// this->operator++(); this->operator++();
// return result; return result;
// } }
// bool operator==(const conditional_iterator_impl &rhs) const { return mBegin == rhs.mBegin; } bool operator==(const conditional_iterator_impl &rhs) const { return mBegin == rhs.mBegin; }
// bool operator!=(const conditional_iterator_impl &rhs) const { return mBegin != rhs.mBegin; } bool operator!=(const conditional_iterator_impl &rhs) const { return mBegin != rhs.mBegin; }
// template <typename IRowType, typename... ITs> template <typename IRowType, typename... ITs>
// bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const { return mBegin == rhs; } bool operator==(const iterator_impl<IRowType, ITs...> &rhs) const { return mBegin == rhs; }
// template <typename IRowType, typename... ITs> template <typename IRowType, typename... ITs>
// bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const { return mBegin != rhs; } bool operator!=(const iterator_impl<IRowType, ITs...> &rhs) const { return mBegin != rhs; }
// private: private:
// CategoryType *mCat; CategoryType *mCat;
// base_iterator mBegin, mEnd; base_iterator mBegin, mEnd;
// const Condition *mCondition; const condition *m_condition;
// }; };
// using iterator = conditional_iterator_impl; using iterator = conditional_iterator_impl;
// using reference = typename iterator::reference; using reference = typename iterator::reference;
// template <typename... Ns> template <typename... Ns>
// conditional_iterator_proxy(CategoryType &cat, row_iterator pos, Condition &&cond, Ns... names); conditional_iterator_proxy(CategoryType &cat, row_iterator pos, condition &&cond, Ns... names);
// conditional_iterator_proxy(conditional_iterator_proxy &&p); conditional_iterator_proxy(conditional_iterator_proxy &&p);
// conditional_iterator_proxy &operator=(conditional_iterator_proxy &&p); conditional_iterator_proxy &operator=(conditional_iterator_proxy &&p);
// conditional_iterator_proxy(const conditional_iterator_proxy &) = delete; conditional_iterator_proxy(const conditional_iterator_proxy &) = delete;
// conditional_iterator_proxy &operator=(const conditional_iterator_proxy &) = delete; conditional_iterator_proxy &operator=(const conditional_iterator_proxy &) = delete;
// iterator begin() const; iterator begin() const;
// iterator end() const; iterator end() const;
// bool empty() const; bool empty() const;
// explicit operator bool() const { return not empty(); } explicit operator bool() const { return not empty(); }
// size_t size() const { return std::distance(begin(), end()); } size_t size() const { return std::distance(begin(), end()); }
// row_type front() { return *begin(); } // row front() { return *begin(); }
// CategoryType &category() const { return *mCat; } CategoryType &category() const { return *mCat; }
// void swap(conditional_iterator_proxy &rhs); void swap(conditional_iterator_proxy &rhs);
// private: private:
// CategoryType *mCat; CategoryType *mCat;
// Condition mCondition; condition m_condition;
// row_iterator mCBegin, mCEnd; row_iterator mCBegin, mCEnd;
// std::array<size_t, N> mCix; std::array<size_t, N> mCix;
// }; };
// -------------------------------------------------------------------- // --------------------------------------------------------------------
...@@ -385,80 +389,80 @@ iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos, ...@@ -385,80 +389,80 @@ iterator_proxy<Category, Ts...>::iterator_proxy(Category &cat, row_iterator pos,
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// template <typename Category, typename... Ts> template <typename Category, typename... Ts>
// conditional_iterator_proxy<Category, Ts...>::conditional_iterator_impl::conditional_iterator_impl( conditional_iterator_proxy<Category, Ts...>::conditional_iterator_impl::conditional_iterator_impl(
// Category &cat, row_iterator pos, const Condition &cond, const std::array<size_t, N> &cix) Category &cat, row_iterator pos, const condition &cond, const std::array<size_t, N> &cix)
// : mCat(&cat) : mCat(&cat)
// , mBegin(pos, cix) , mBegin(pos, cix)
// , mEnd(cat.end(), cix) , mEnd(cat.end(), cix)
// , mCondition(&cond) , m_condition(&cond)
// { {
// } }
// template <typename Category, typename... Ts> template <typename Category, typename... Ts>
// conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(conditional_iterator_proxy &&p) conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(conditional_iterator_proxy &&p)
// : mCat(nullptr) : mCat(nullptr)
// , mCBegin(p.mCBegin) , mCBegin(p.mCBegin)
// , mCEnd(p.mCEnd) , mCEnd(p.mCEnd)
// , mCix(p.mCix) , mCix(p.mCix)
// { {
// std::swap(mCat, p.mCat); std::swap(mCat, p.mCat);
// std::swap(mCix, p.mCix); std::swap(mCix, p.mCix);
// mCondition.swap(p.mCondition); m_condition.swap(p.m_condition);
// } }
// template <typename Category, typename... Ts> template <typename Category, typename... Ts>
// template <typename... Ns> template <typename... Ns>
// conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(Category &cat, row_iterator pos, Condition &&cond, Ns... names) conditional_iterator_proxy<Category, Ts...>::conditional_iterator_proxy(Category &cat, row_iterator pos, condition &&cond, Ns... names)
// : mCat(&cat) : mCat(&cat)
// , mCondition(std::move(cond)) , m_condition(std::move(cond))
// , mCBegin(pos) , mCBegin(pos)
// , mCEnd(cat.end()) , mCEnd(cat.end())
// { {
// static_assert(sizeof...(Ts) == sizeof...(Ns), "Number of column names should be equal to number of requested value types"); static_assert(sizeof...(Ts) == sizeof...(Ns), "Number of column names should be equal to number of requested value types");
// mCondition.prepare(cat); m_condition.prepare(cat);
// while (mCBegin != mCEnd and not mCondition(*mCat, mCBegin.row())) while (mCBegin != mCEnd and not m_condition(*mCBegin))
// ++mCBegin; ++mCBegin;
// size_t i = 0; size_t i = 0;
// ((mCix[i++] = mCat->getColumnIndex(names)), ...); ((mCix[i++] = mCat->getColumnIndex(names)), ...);
// } }
// template <typename Category, typename... Ts> template <typename Category, typename... Ts>
// conditional_iterator_proxy<Category, Ts...> &conditional_iterator_proxy<Category, Ts...>::operator=(conditional_iterator_proxy &&p) conditional_iterator_proxy<Category, Ts...> &conditional_iterator_proxy<Category, Ts...>::operator=(conditional_iterator_proxy &&p)
// { {
// swap(p); swap(p);
// return *this; return *this;
// } }
// template <typename Category, typename... Ts> template <typename Category, typename... Ts>
// typename conditional_iterator_proxy<Category, Ts...>::iterator conditional_iterator_proxy<Category, Ts...>::begin() const typename conditional_iterator_proxy<Category, Ts...>::iterator conditional_iterator_proxy<Category, Ts...>::begin() const
// { {
// return iterator(*mCat, mCBegin, mCondition, mCix); return iterator(*mCat, mCBegin, m_condition, mCix);
// } }
// template <typename Category, typename... Ts> template <typename Category, typename... Ts>
// typename conditional_iterator_proxy<Category, Ts...>::iterator conditional_iterator_proxy<Category, Ts...>::end() const typename conditional_iterator_proxy<Category, Ts...>::iterator conditional_iterator_proxy<Category, Ts...>::end() const
// { {
// return iterator(*mCat, mCEnd, mCondition, mCix); return iterator(*mCat, mCEnd, m_condition, mCix);
// } }
// template <typename Category, typename... Ts> template <typename Category, typename... Ts>
// bool conditional_iterator_proxy<Category, Ts...>::empty() const bool conditional_iterator_proxy<Category, Ts...>::empty() const
// { {
// return mCBegin == mCEnd; return mCBegin == mCEnd;
// } }
// template <typename Category, typename... Ts> template <typename Category, typename... Ts>
// void conditional_iterator_proxy<Category, Ts...>::swap(conditional_iterator_proxy &rhs) void conditional_iterator_proxy<Category, Ts...>::swap(conditional_iterator_proxy &rhs)
// { {
// std::swap(mCat, rhs.mCat); std::swap(mCat, rhs.mCat);
// mCondition.swap(rhs.mCondition); m_condition.swap(rhs.m_condition);
// std::swap(mCBegin, rhs.mCBegin); std::swap(mCBegin, rhs.mCBegin);
// std::swap(mCEnd, rhs.mCEnd); std::swap(mCEnd, rhs.mCEnd);
// std::swap(mCix, rhs.mCix); std::swap(mCix, rhs.mCix);
// } }
} // namespace cif::v2 } // namespace cif::v2
\ No newline at end of file
...@@ -26,19 +26,7 @@ ...@@ -26,19 +26,7 @@
#pragma once #pragma once
#include <cassert> #include <cif++/v2/row.hpp>
#include <iostream>
#include <map>
#include <stack>
#include <regex>
#include <cif++/v2/forward_decl.hpp>
#include <cif++/CifUtils.hpp>
namespace cif
{
extern int VERBOSE;
}
namespace cif::v2 namespace cif::v2
{ {
...@@ -61,16 +49,7 @@ class sac_parser ...@@ -61,16 +49,7 @@ class sac_parser
public: public:
using datablock_index = std::map<std::string, std::size_t>; using datablock_index = std::map<std::string, std::size_t>;
sac_parser(std::istream &is, bool init = true) sac_parser(std::istream &is, bool init = true);
: m_source(is)
{
m_validate = true;
m_line_nr = 1;
m_bol = true;
if (init)
m_lookahead = get_next_token();
}
virtual ~sac_parser() = default; virtual ~sac_parser() = default;
...@@ -201,719 +180,31 @@ class sac_parser ...@@ -201,719 +180,31 @@ class sac_parser
// get_next_char takes a char from the buffer, or if it is empty // get_next_char takes a char from the buffer, or if it is empty
// from the istream. This function also does carriage/linefeed // from the istream. This function also does carriage/linefeed
// translation. // translation.
int get_next_char() int get_next_char();
{
int result;
if (m_buffer.empty())
result = m_source.get();
else
{
result = m_buffer.top();
m_buffer.pop();
}
// very simple CR/LF translation into LF
if (result == '\r')
{
int lookahead = m_source.get();
if (lookahead != '\n')
m_buffer.push(lookahead);
result = '\n';
}
m_token_value += static_cast<char>(result);
if (result == '\n') void retract();
++m_line_nr;
if (VERBOSE >= 6)
{
std::cerr << "get_next_char => ";
if (iscntrl(result) or not isprint(result))
std::cerr << int(result) << std::endl;
else
std::cerr << char(result) << std::endl;
}
return result;
}
void retract()
{
assert(not m_token_value.empty());
char ch = m_token_value.back(); int restart(int start);
if (ch == '\n')
--m_line_nr;
m_buffer.push(ch);
m_token_value.pop_back();
}
int restart(int start)
{
int result = 0;
while (not m_token_value.empty())
retract();
switch (start)
{
case State::Start:
result = State::Float;
break;
case State::Float:
result = State::Int;
break;
case State::Int:
result = State::Value;
break;
default:
error("Invalid state in SacParser");
}
m_bol = false;
return result;
}
CIFToken get_next_token()
{
const auto kEOF = std::char_traits<char>::eof();
CIFToken result = CIFToken::Unknown;
int quoteChar = 0;
int state = State::Start, start = State::Start;
m_bol = false;
m_token_value.clear();
mTokenType = CIFValue::Unknown;
while (result == CIFToken::Unknown)
{
auto ch = get_next_char();
switch (state) CIFToken get_next_token();
{
case State::Start:
if (ch == kEOF)
result = CIFToken::Eof;
else if (ch == '\n')
{
m_bol = true;
state = State::White;
}
else if (ch == ' ' or ch == '\t')
state = State::White;
else if (ch == '#')
state = State::Comment;
else if (ch == '_')
state = State::Tag;
else if (ch == ';' and m_bol)
state = State::TextField;
else if (ch == '\'' or ch == '"')
{
quoteChar = ch;
state = State::QuotedString;
}
else
state = start = restart(start);
break;
case State::White: void match(CIFToken token);
if (ch == kEOF)
result = CIFToken::Eof;
else if (not isspace(ch))
{
state = State::Start;
retract();
m_token_value.clear();
}
else
m_bol = (ch == '\n');
break;
case State::Comment:
if (ch == '\n')
{
state = State::Start;
m_bol = true;
m_token_value.clear();
}
else if (ch == kEOF)
result = CIFToken::Eof;
else if (not is_any_print(ch))
error("invalid character in comment");
break;
case State::TextField:
if (ch == '\n')
state = State::TextField + 1;
else if (ch == kEOF)
error("unterminated textfield");
else if (not is_any_print(ch))
warning("invalid character in text field '" + std::string({static_cast<char>(ch)}) + "' (" + std::to_string((int)ch) + ")");
break;
case State::TextField + 1:
if (is_text_lead(ch) or ch == ' ' or ch == '\t')
state = State::TextField;
else if (ch == ';')
{
assert(m_token_value.length() >= 2);
m_token_value = m_token_value.substr(1, m_token_value.length() - 3);
mTokenType = CIFValue::TextField;
result = CIFToken::Value;
}
else if (ch == kEOF)
error("unterminated textfield");
else if (ch != '\n')
error("invalid character in text field");
break;
case State::QuotedString:
if (ch == kEOF)
error("unterminated quoted string");
else if (ch == quoteChar)
state = State::QuotedStringQuote;
else if (not is_any_print(ch))
warning("invalid character in quoted string: '" + std::string({static_cast<char>(ch)}) + '\'');
break;
case State::QuotedStringQuote:
if (is_white(ch))
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::String;
if (m_token_value.length() < 2)
error("Invalid quoted string token");
m_token_value = m_token_value.substr(1, m_token_value.length() - 2);
}
else if (ch == quoteChar)
;
else if (is_any_print(ch))
state = State::QuotedString;
else if (ch == kEOF)
error("unterminated quoted string");
else
error("invalid character in quoted string");
break;
case State::Tag:
if (not is_non_blank(ch))
{
retract();
result = CIFToken::Tag;
}
break;
case State::Float:
if (ch == '+' or ch == '-')
{
state = State::Float + 1;
}
else if (isdigit(ch))
state = State::Float + 1;
else
state = start = restart(start);
break;
case State::Float + 1:
// if (ch == '(') // numeric???
// mState = State::NumericSuffix;
// else
if (ch == '.')
state = State::Float + 2;
else if (tolower(ch) == 'e')
state = State::Float + 3;
else if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Int;
}
else
state = start = restart(start);
break;
// parsed '.'
case State::Float + 2:
if (tolower(ch) == 'e')
state = State::Float + 3;
else if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Float;
}
else
state = start = restart(start);
break;
// parsed 'e'
case State::Float + 3:
if (ch == '-' or ch == '+')
state = State::Float + 4;
else if (isdigit(ch))
state = State::Float + 5;
else
state = start = restart(start);
break;
case State::Float + 4:
if (isdigit(ch))
state = State::Float + 5;
else
state = start = restart(start);
break;
case State::Float + 5:
if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Float;
}
else
state = start = restart(start);
break;
case State::Int:
if (isdigit(ch) or ch == '+' or ch == '-')
state = State::Int + 1;
else
state = start = restart(start);
break;
case State::Int + 1:
if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Int;
}
else
state = start = restart(start);
break;
case State::Value:
if (ch == '_')
{
std::string s = toLowerCopy(m_token_value);
if (s == "global_")
result = CIFToken::GLOBAL;
else if (s == "stop_")
result = CIFToken::STOP;
else if (s == "loop_")
result = CIFToken::LOOP;
else if (s == "data_")
{
state = State::DATA;
continue;
}
else if (s == "save_")
{
state = State::SAVE;
continue;
}
}
if (result == CIFToken::Unknown and not is_non_blank(ch))
{
retract();
result = CIFToken::Value;
if (m_token_value == ".")
mTokenType = CIFValue::Inapplicable;
else if (m_token_value == "?")
{
mTokenType = CIFValue::Unknown;
m_token_value.clear();
}
}
break;
case State::DATA:
case State::SAVE:
if (not is_non_blank(ch))
{
retract();
if (state == State::DATA)
result = CIFToken::DATA;
else
result = CIFToken::SAVE;
m_token_value.erase(m_token_value.begin(), m_token_value.begin() + 5);
}
break;
default:
assert(false);
error("Invalid state in get_next_token");
break;
}
}
if (VERBOSE >= 5)
{
std::cerr << get_token_name(result);
if (mTokenType != CIFValue::Unknown)
std::cerr << ' ' << get_value_name(mTokenType);
if (result != CIFToken::Eof)
std::cerr << " " << std::quoted(m_token_value);
std::cerr << std::endl;
}
return result;
}
void match(CIFToken token)
{
if (m_lookahead != token)
error(std::string("Unexpected token, expected ") + get_token_name(token) + " but found " + get_token_name(m_lookahead));
m_lookahead = get_next_token();
}
public: public:
bool parse_single_datablock(const std::string &datablock) bool parse_single_datablock(const std::string &datablock);
{
// first locate the start, as fast as we can
auto &sb = *m_source.rdbuf();
enum
{
start,
comment,
string,
string_quote,
qstring,
data
} state = start;
int quote = 0;
bool bol = true;
std::string dblk = "data_" + datablock;
std::string::size_type si = 0;
bool found = false;
for (auto ch = sb.sbumpc(); not found and ch != std::streambuf::traits_type::eof(); ch = sb.sbumpc())
{
switch (state)
{
case start:
switch (ch)
{
case '#': state = comment; break;
case 'd':
case 'D':
state = data;
si = 1;
break;
case '\'':
case '"':
state = string;
quote = ch;
break;
case ';':
if (bol)
state = qstring;
break;
}
break;
case comment:
if (ch == '\n')
state = start;
break;
case string: datablock_index index_datablocks();
if (ch == quote)
state = string_quote;
break;
case string_quote: bool parse_single_datablock(const std::string &datablock, const datablock_index &index);
if (std::isspace(ch))
state = start;
else
state = string;
break;
case qstring: void parse_file();
if (ch == ';' and bol)
state = start;
break;
case data:
if (isspace(ch) and dblk[si] == 0)
found = true;
else if (dblk[si++] != ch)
state = start;
break;
}
bol = (ch == '\n');
}
if (found)
{
produce_datablock(datablock);
m_lookahead = get_next_token();
parse_datablock();
}
return found;
}
datablock_index index_datablocks()
{
datablock_index index;
// first locate the start, as fast as we can
auto &sb = *m_source.rdbuf();
enum
{
start,
comment,
string,
string_quote,
qstring,
data,
data_name
} state = start;
int quote = 0;
bool bol = true;
const char dblk[] = "data_";
std::string::size_type si = 0;
std::string datablock;
for (auto ch = sb.sbumpc(); ch != std::streambuf::traits_type::eof(); ch = sb.sbumpc())
{
switch (state)
{
case start:
switch (ch)
{
case '#': state = comment; break;
case 'd':
case 'D':
state = data;
si = 1;
break;
case '\'':
case '"':
state = string;
quote = ch;
break;
case ';':
if (bol)
state = qstring;
break;
}
break;
case comment:
if (ch == '\n')
state = start;
break;
case string:
if (ch == quote)
state = string_quote;
break;
case string_quote:
if (std::isspace(ch))
state = start;
else
state = string;
break;
case qstring:
if (ch == ';' and bol)
state = start;
break;
case data:
if (dblk[si] == 0 and is_non_blank(ch))
{
datablock = {static_cast<char>(ch)};
state = data_name;
}
else if (dblk[si++] != ch)
state = start;
break;
case data_name:
if (is_non_blank(ch))
datablock.insert(datablock.end(), char(ch));
else if (isspace(ch))
{
if (not datablock.empty())
index[datablock] = m_source.tellg();
state = start;
}
else
state = start;
break;
}
bol = (ch == '\n');
}
return index;
}
bool parse_single_datablock(const std::string &datablock, const datablock_index &index)
{
bool result = false;
auto i = index.find(datablock);
if (i != index.end())
{
m_source.seekg(i->second);
produce_datablock(datablock);
m_lookahead = get_next_token();
parse_datablock();
result = true;
}
return result;
}
void parse_file()
{
while (m_lookahead != CIFToken::Eof)
{
switch (m_lookahead)
{
case CIFToken::GLOBAL:
parse_global();
break;
case CIFToken::DATA:
produce_datablock(m_token_value);
match(CIFToken::DATA);
parse_datablock();
break;
default:
error("This file does not seem to be an mmCIF file");
break;
}
}
}
protected: protected:
void parse_global() void parse_global();
{
match(CIFToken::GLOBAL);
while (m_lookahead == CIFToken::Tag)
{
match(CIFToken::Tag);
match(CIFToken::Value);
}
}
void parse_datablock()
{
std::string cat;
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag or m_lookahead == CIFToken::SAVE)
{
switch (m_lookahead)
{
case CIFToken::LOOP:
{
cat.clear(); // should start a new category
match(CIFToken::LOOP);
std::vector<std::string> tags;
while (m_lookahead == CIFToken::Tag)
{
std::string catName, itemName;
std::tie(catName, itemName) = splitTagName(m_token_value);
if (cat.empty())
{
produce_category(catName);
cat = catName;
}
else if (not iequals(cat, catName))
error("inconsistent categories in loop_");
tags.push_back(itemName);
match(CIFToken::Tag);
}
while (m_lookahead == CIFToken::Value)
{
produce_row();
for (auto tag : tags)
{
produce_item(cat, tag, m_token_value);
match(CIFToken::Value);
}
}
cat.clear();
break;
}
case CIFToken::Tag:
{
std::string catName, itemName;
std::tie(catName, itemName) = splitTagName(m_token_value);
if (not iequals(cat, catName))
{
produce_category(catName);
cat = catName;
produce_row();
}
match(CIFToken::Tag); void parse_datablock();
produce_item(cat, itemName, m_token_value); virtual void parse_save_frame();
match(CIFToken::Value);
break;
}
case CIFToken::SAVE:
parse_save_frame();
break;
default:
assert(false);
break;
}
}
}
virtual void parse_save_frame()
{
error("A regular CIF file should not contain a save frame");
}
void error(const std::string &msg) void error(const std::string &msg)
{ {
...@@ -966,61 +257,28 @@ class sac_parser ...@@ -966,61 +257,28 @@ class sac_parser
// -------------------------------------------------------------------- // --------------------------------------------------------------------
template <typename Alloc, typename File, typename Datablock, typename Category> class parser : public sac_parser
class parser_t : public sac_parser
{ {
public: public:
using file_type = File; parser(std::istream &is, file &file)
using datablock_type = Datablock;
using category_type = Category;
using row_handle_type = category_type::reference;
parser_t(std::istream &is, file_type &file)
: sac_parser(is) : sac_parser(is)
, m_file(file) , m_file(file)
{ {
} }
void produce_datablock(const std::string &name) override void produce_datablock(const std::string &name) override;
{
const auto &[iter, ignore] = m_file.emplace(name);
m_datablock = &(*iter);
}
void produce_category(const std::string &name) override void produce_category(const std::string &name) override;
{
if (VERBOSE >= 4)
std::cerr << "producing category " << name << std::endl;
std::tie(m_category, std::ignore) = m_datablock->emplace(name);
}
void produce_row() override
{
if (VERBOSE >= 4)
std::cerr << "producing row for category " << m_category->name() << std::endl;
m_category->emplace({}); void produce_row() override;
m_row = m_category->back();
// m_row.lineNr(m_line_nr);
}
void produce_item(const std::string &category, const std::string &item, const std::string &value) override
{
if (VERBOSE >= 4)
std::cerr << "producing _" << category << '.' << item << " -> " << value << std::endl;
if (not iequals(category, m_category->name())) void produce_item(const std::string &category, const std::string &item, const std::string &value) override;
error("inconsistent categories in loop_");
m_row[item] = m_token_value;
}
protected: protected:
file_type &m_file; file &m_file;
datablock_type *m_datablock; datablock *m_datablock = nullptr;
datablock_type::iterator m_category; category *m_category = nullptr;
row_handle_type m_row; row_handle m_row;
}; };
} // namespace cif::v2 } // namespace cif::v2
...@@ -27,37 +27,26 @@ ...@@ -27,37 +27,26 @@
#pragma once #pragma once
#include <cif++/v2/item.hpp> #include <cif++/v2/item.hpp>
#include <cif++/v2/condition.hpp>
namespace cif::v2 namespace cif::v2
{ {
template <typename>
class row_handle;
namespace detail namespace detail
{ {
// some helper classes to help create tuple result types // some helper classes to help create tuple result types
template < template <typename... C>
typename Category,
typename... C>
struct get_row_result struct get_row_result
{ {
using category_type = Category;
using row_type = category_type::row;
using row_handle_type = row_handle<category_type>;
using item_handle_type = item_handle<row_type>;
static constexpr size_t N = sizeof...(C); static constexpr size_t N = sizeof...(C);
get_row_result(const row_handle_type &r, std::array<size_t, N> &&columns) get_row_result(const row_handle &r, std::array<size_t, N> &&columns)
: m_row(r) : m_row(r)
, m_columns(std::move(columns)) , m_columns(std::move(columns))
{ {
} }
const item_handle_type operator[](size_t ix) const const item_handle operator[](size_t ix) const
{ {
return m_row[m_columns[ix]]; return m_row[m_columns[ix]];
} }
...@@ -74,7 +63,7 @@ namespace detail ...@@ -74,7 +63,7 @@ namespace detail
return std::tuple<Ts...>{m_row[m_columns[Is]].template as<Ts>()...}; return std::tuple<Ts...>{m_row[m_columns[Is]].template as<Ts>()...};
} }
const row_handle_type &m_row; const row_handle &m_row;
std::array<size_t, N> m_columns; std::array<size_t, N> m_columns;
}; };
...@@ -112,21 +101,39 @@ auto tie(Ts &...v) ...@@ -112,21 +101,39 @@ auto tie(Ts &...v)
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------
/// \brief row_handle is the way to access data in rows /// \brief the row class, this one is not directly accessible from the outside
template <typename Category> class row
class row_handle
{ {
public: public:
using category_type = Category; row() = default;
using row_type = std::conditional_t<std::is_const_v<category_type>, const typename category_type::row, typename category_type::row>;
using item_handle_type = item_handle<row_handle>; private:
friend class item_handle;
template <typename> template <typename, typename...>
friend class row_handle; friend class iterator_impl;
friend class category;
void append(item_value *iv)
{
if (m_head == nullptr)
m_head = m_tail = iv;
else
m_tail = m_tail->m_next = iv;
}
row *m_next = nullptr;
item_value *m_head = nullptr, *m_tail = nullptr;
};
template <typename> // --------------------------------------------------------------------
/// \brief row_handle is the way to access data in rows
class row_handle
{
public:
friend class item_handle; friend class item_handle;
row_handle() = default; row_handle() = default;
...@@ -137,42 +144,40 @@ class row_handle ...@@ -137,42 +144,40 @@ class row_handle
row_handle &operator=(const row_handle &) = default; row_handle &operator=(const row_handle &) = default;
row_handle &operator=(row_handle &&) = default; row_handle &operator=(row_handle &&) = default;
template <typename C2> row_handle(const category &cat, const row &r)
row_handle(const row_handle<C2> &rhs) : m_category(const_cast<category *>(&cat))
: m_cat(rhs.m_cat) , m_row(const_cast<row *>(&r))
, m_row(rhs.m_row)
{ {
} }
row_handle(category_type &cat, row_type &row) const category &cat() const
: m_cat(&cat)
, m_row(&row)
{ {
return *m_category;
} }
explicit operator bool() const explicit operator bool() const
{ {
return m_cat != nullptr and m_row != nullptr; return m_category != nullptr and m_row != nullptr;
} }
item_handle_type operator[](uint32_t column_ix) item_handle operator[](uint32_t column_ix)
{ {
return item_handle_type(column_ix, *this); return item_handle(column_ix, *this);
} }
const item_handle_type operator[](uint32_t column_ix) const const item_handle operator[](uint32_t column_ix) const
{ {
return item_handle_type(column_ix, const_cast<row_handle &>(*this)); return item_handle(column_ix, const_cast<row_handle &>(*this));
} }
item_handle_type operator[](std::string_view column_name) item_handle operator[](std::string_view column_name)
{ {
return item_handle_type(add_column(column_name), *this); return item_handle(add_column(column_name), *this);
} }
const item_handle_type operator[](std::string_view column_name) const const item_handle operator[](std::string_view column_name) const
{ {
return item_handle_type(get_column_ix(column_name), *this); return item_handle(get_column_ix(column_name), const_cast<row_handle &>(*this));
} }
template <typename... Ts, size_t N> template <typename... Ts, size_t N>
...@@ -183,13 +188,13 @@ class row_handle ...@@ -183,13 +188,13 @@ class row_handle
std::array<size_t, N> cix; std::array<size_t, N> cix;
for (size_t i = 0; i < N; ++i) for (size_t i = 0; i < N; ++i)
cix[i] = get_column_ix(columns[i]); cix[i] = get_column_ix(columns[i]);
return detail::get_row_result<category_type, Ts...>(*this, std::move(cix)); return detail::get_row_result<Ts...>(*this, std::move(cix));
} }
template <typename... C> template <typename... C>
auto get(C... columns) const auto get(C... columns) const
{ {
return detail::get_row_result<category_type, C...>(*this, {get_column_ix(columns)...}); return detail::get_row_result<C...>(*this, {get_column_ix(columns)...});
} }
void assign(const std::vector<item> &values) void assign(const std::vector<item> &values)
...@@ -247,32 +252,23 @@ class row_handle ...@@ -247,32 +252,23 @@ class row_handle
void assign(std::string_view name, std::string_view value, bool updateLinked, bool validate = true) void assign(std::string_view name, std::string_view value, bool updateLinked, bool validate = true)
{ {
assign(m_cat->add_column(name), value, updateLinked, validate); assign(add_column(name), value, updateLinked, validate);
} }
void assign(size_t column, std::string_view value, bool updateLinked, bool validate = true) void assign(size_t column, std::string_view value, bool updateLinked, bool validate = true);
{
m_cat->update_value(m_row, column, value, updateLinked, validate);
}
private: private:
uint16_t get_column_ix(std::string_view name) const uint16_t get_column_ix(std::string_view name) const;
{
return m_cat->get_column_ix(name);
}
uint16_t add_column(std::string_view name) uint16_t add_column(std::string_view name);
{
return m_cat->add_column(name);
}
void assign(const item &i, bool updateLinked) void assign(const item &i, bool updateLinked)
{ {
assign(i.name(), i.value(), updateLinked); assign(i.name(), i.value(), updateLinked);
} }
category_type *m_cat = nullptr; category *m_category = nullptr;
row_type *m_row = nullptr; row *m_row = nullptr;
}; };
} // namespace cif::v2 } // namespace cif::v2
\ No newline at end of file
...@@ -166,7 +166,7 @@ class Validator ...@@ -166,7 +166,7 @@ class Validator
{ {
} }
~Validator(); ~Validator() = default;
Validator(const Validator &rhs) = delete; Validator(const Validator &rhs) = delete;
Validator &operator=(const Validator &rhs) = delete; Validator &operator=(const Validator &rhs) = delete;
...@@ -175,7 +175,6 @@ class Validator ...@@ -175,7 +175,6 @@ class Validator
Validator &operator=(Validator &&rhs); Validator &operator=(Validator &&rhs);
friend class dictionary_parser; friend class dictionary_parser;
// friend class ValidatorFactory;
void addTypeValidator(ValidateType &&v); void addTypeValidator(ValidateType &&v);
const ValidateType *getValidatorForType(std::string_view typeCode) const; const ValidateType *getValidatorForType(std::string_view typeCode) const;
...@@ -189,11 +188,11 @@ class Validator ...@@ -189,11 +188,11 @@ class Validator
void reportError(const std::string &msg, bool fatal) const; void reportError(const std::string &msg, bool fatal) const;
const std::string &dictName() const { return m_name; } const std::string &name() const { return m_name; }
void dictName(const std::string &name) { m_name = name; } void set_name(const std::string &name) { m_name = name; }
const std::string &dictVersion() const { return m_version; } const std::string &version() const { return m_version; }
void dictVersion(const std::string &version) { m_version = version; } void version(const std::string &version) { m_version = version; }
private: private:
// name is fully qualified here: // name is fully qualified here:
...@@ -201,7 +200,7 @@ class Validator ...@@ -201,7 +200,7 @@ class Validator
std::string m_name; std::string m_name;
std::string m_version; std::string m_version;
bool mStrict = false; bool m_strict = false;
std::set<ValidateType> mTypeValidators; std::set<ValidateType> mTypeValidators;
std::set<ValidateCategory> mCategoryValidators; std::set<ValidateCategory> mCategoryValidators;
std::vector<ValidateLink> mLinkValidators; std::vector<ValidateLink> mLinkValidators;
......
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cif++/v2/category.hpp>
namespace cif::v2
{
void category::update_value(row *row, size_t column, std::string_view value, bool updateLinked, bool validate)
{
auto &col = m_columns[column];
const char *oldValue = nullptr;
for (auto iv = row->m_head; iv != nullptr; iv = iv->m_next)
{
assert(iv != iv->m_next and (iv->m_next == nullptr or iv != iv->m_next->m_next));
if (iv->m_column_ix == column)
{
oldValue = iv->c_str();
break;
}
}
if (oldValue != nullptr and value == oldValue) // no need to update
return;
std::string oldStrValue = oldValue ? oldValue : "";
// // check the value
// if (col.m_validator and validate)
// (*col.m_validator)(value);
// If the field is part of the Key for this Category, remove it from the index
// before updating
bool reinsert = false;
// if (updateLinked and // an update of an Item's value
// cat->mIndex != nullptr and cat->keyFieldsByIndex().count(column))
// {
// reinsert = cat->mIndex->find(mData);
// if (reinsert)
// cat->mIndex->erase(mData);
// }
// first remove old value with cix
if (row->m_head == nullptr)
; // nothing to do
else if (row->m_head->m_column_ix == column)
{
auto iv = row->m_head;
row->m_head = iv->m_next;
iv->m_next = nullptr;
delete_item(iv);
}
else
{
for (auto iv = row->m_head; iv->m_next != nullptr; iv = iv->m_next)
{
if (iv->m_next->m_column_ix != column)
continue;
auto nv = iv->m_next;
iv->m_next = nv->m_next;
nv->m_next = nullptr;
delete_item(nv);
break;
}
}
if (not value.empty())
{
auto nv = create_item(column, value);
if (row->m_head == nullptr)
row->m_head = nv;
else
{
auto iv = row->m_head;
while (iv->m_next != nullptr)
iv = iv->m_next;
iv->m_next = nv;
}
}
// if (reinsert)
// cat->mIndex->insert(mData);
// // see if we need to update any child categories that depend on this value
// auto iv = col.m_validator;
// if (not skipUpdateLinked and iv != nullptr and mCascade)
// {
// for (auto &&[childCat, linked] : cat->mChildLinks)
// {
// if (find(linked->mParentKeys.begin(), linked->mParentKeys.end(), iv->mTag) == linked->mParentKeys.end())
// continue;
// Condition cond;
// std::string childTag;
// for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
// {
// std::string pk = linked->mParentKeys[ix];
// std::string ck = linked->mChildKeys[ix];
// // TODO add code to *NOT* test mandatory fields for Empty
// if (pk == iv->mTag)
// {
// childTag = ck;
// cond = std::move(cond) && Key(ck) == oldStrValue;
// }
// else
// {
// const char *pk_value = (*this)[pk].c_str();
// if (*pk_value == 0)
// cond = std::move(cond) && Key(ck) == Empty();
// else
// cond = std::move(cond) && ((Key(ck) == pk_value) or Key(ck) == Empty());
// }
// }
// auto rows = childCat->find(std::move(cond));
// if (rows.empty())
// continue;
// // if (cif::VERBOSE > 2)
// // {
// // std::cerr << "Parent: " << linked->mParentCategory << " Child: " << linked->mChildCategory << std::endl
// // << cond << std::endl;
// // }
// // Now, suppose there are already rows in child that conform to the new value,
// // we then skip this renam
// Condition cond_n;
// for (size_t ix = 0; ix < linked->mParentKeys.size(); ++ix)
// {
// std::string pk = linked->mParentKeys[ix];
// std::string ck = linked->mChildKeys[ix];
// // TODO add code to *NOT* test mandatory fields for Empty
// if (pk == iv->mTag)
// cond_n = std::move(cond_n) && Key(ck) == value;
// else
// {
// const char *pk_value = (*this)[pk].c_str();
// if (*pk_value == 0)
// cond_n = std::move(cond_n) && Key(ck) == Empty();
// else
// cond_n = std::move(cond_n) && ((Key(ck) == pk_value) or Key(ck) == Empty());
// }
// }
// auto rows_n = childCat->find(std::move(cond_n));
// if (not rows_n.empty())
// {
// if (cif::VERBOSE > 0)
// std::cerr << "Will not rename in child category since there are already rows that link to the parent" << std::endl;
// continue;
// }
// for (auto &cr : rows)
// cr.assign(childTag, value, false);
// }
// }
}
// proxy methods for every insertion
category::iterator category::insert_impl(const_iterator pos, row *n)
{
assert(n != nullptr);
assert(n->m_next == nullptr);
if (n == nullptr)
throw std::runtime_error("Invalid pointer passed to insert");
// insert at end, most often this is the case
if (pos.m_current == nullptr)
{
if (m_head == nullptr)
m_tail = m_head = n;
else
m_tail = m_tail->m_next = n;
}
else
{
assert(m_head != nullptr);
if (pos.m_current == m_head)
m_head = n->m_next = m_head;
else
n = n->m_next = m_head->m_next;
}
return iterator(*this, n);
}
category::iterator category::erase_impl(const_iterator pos)
{
if (pos == cend())
return end();
assert(false);
// TODO: implement
// row *n = const_cast<row *>(pos.row());
// row *cur;
// if (m_head == n)
// {
// m_head = static_cast<row *>(m_head->m_next);
// if (m_head == nullptr)
// m_tail = nullptr;
// n->m_next = nullptr;
// delete_row(n);
// cur = m_head;
// }
// else
// {
// cur = static_cast<row *>(n->m_next);
// if (m_tail == n)
// m_tail = static_cast<row *>(n->m_prev);
// row *p = m_head;
// while (p != nullptr and p->m_next != n)
// p = p->m_next;
// if (p != nullptr and p->m_next == n)
// {
// p->m_next = n->m_next;
// if (p->m_next != nullptr)
// p->m_next->m_prev = p;
// n->m_next = nullptr;
// }
// else
// throw std::runtime_error("remove for a row not found in the list");
// delete_row(n);
// }
// return iterator(*this, cur);
}
std::vector<std::string> get_category_fields(const category &cat)
{
return {};
}
} // namespace cif::v2
\ No newline at end of file
...@@ -34,23 +34,24 @@ namespace cif::v2 ...@@ -34,23 +34,24 @@ namespace cif::v2
using namespace literals; using namespace literals;
class dictionary_parser : public parser_t<> inline void replace_all(std::string &s, std::string_view pat, std::string_view rep)
{ {
public: for (std::string::size_type i = s.find(pat); i != std::string::npos; i = s.find(pat, i))
using base_type = parser_t<>; s.replace(i, pat.size(), rep.data(), rep.size());
using file_type = typename base_type::file_type; }
using datablock_type = typename file_type::datablock_type;
using category_type = typename datablock_type::category_type;
dictionary_parser(Validator &validator, std::istream &is, file_type &f) class dictionary_parser : public parser
: base_type(is, f) {
public:
dictionary_parser(Validator &validator, std::istream &is, file &f)
: parser(is, f)
, m_validator(validator) , m_validator(validator)
{ {
} }
void load_dictionary() void load_dictionary()
{ {
std::unique_ptr<datablock_type> dict; std::unique_ptr<datablock> dict;
auto savedDatablock = m_datablock; auto savedDatablock = m_datablock;
try try
...@@ -65,7 +66,7 @@ class dictionary_parser : public parser_t<> ...@@ -65,7 +66,7 @@ class dictionary_parser : public parser_t<>
default: default:
{ {
dict.reset(new datablock_type(m_token_value)); // dummy datablock, for constructing the validator only dict.reset(new datablock(m_token_value)); // dummy datablock, for constructing the validator only
m_datablock = dict.get(); m_datablock = dict.get();
match(CIFToken::DATA); match(CIFToken::DATA);
...@@ -101,14 +102,14 @@ class dictionary_parser : public parser_t<> ...@@ -101,14 +102,14 @@ class dictionary_parser : public parser_t<>
link_items(); link_items();
// store meta information // store meta information
datablock_type::iterator info; datablock::iterator info;
bool n; bool n;
std::tie(info, n) = m_datablock->emplace("dictionary"); std::tie(info, n) = m_datablock->emplace("dictionary");
if (n) if (n)
{ {
auto r = info->front(); auto r = info->front();
m_validator.dictName(r["title"].as<std::string>()); m_validator.set_name(r["title"].as<std::string>());
m_validator.dictVersion(r["version"].as<std::string>()); m_validator.version(r["version"].as<std::string>());
} }
m_datablock = savedDatablock; m_datablock = savedDatablock;
...@@ -129,8 +130,8 @@ class dictionary_parser : public parser_t<> ...@@ -129,8 +130,8 @@ class dictionary_parser : public parser_t<>
bool isCategorySaveFrame = m_token_value[0] != '_'; bool isCategorySaveFrame = m_token_value[0] != '_';
datablock_type dict(m_token_value); datablock dict(m_token_value);
datablock_type::iterator cat = dict.end(); datablock::iterator cat = dict.end();
match(CIFToken::SAVE); match(CIFToken::SAVE);
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag) while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag)
...@@ -437,14 +438,14 @@ class dictionary_parser : public parser_t<> ...@@ -437,14 +438,14 @@ class dictionary_parser : public parser_t<>
auto &dict = *m_datablock; auto &dict = *m_datablock;
for (auto &t : dict["item_type_list"]) for (auto t : dict["item_type_list"])
{ {
std::string code, primitiveCode, construct; std::string code, primitiveCode, construct;
cif::v2::tie(code, primitiveCode, construct) = t.get("code", "primitive_code", "construct"); cif::v2::tie(code, primitiveCode, construct) = t.get("code", "primitive_code", "construct");
ba::replace_all(construct, "\\n", "\n"); replace_all(construct, "\\n", "\n");
ba::replace_all(construct, "\\t", "\t"); replace_all(construct, "\\t", "\t");
ba::replace_all(construct, "\\\n", ""); replace_all(construct, "\\\n", "");
try try
{ {
...@@ -455,7 +456,7 @@ class dictionary_parser : public parser_t<> ...@@ -455,7 +456,7 @@ class dictionary_parser : public parser_t<>
} }
catch (const std::exception &) catch (const std::exception &)
{ {
std::throw_with_nested(CifParserError(t.lineNr(), "error in regular expression")); std::throw_with_nested(parse_error(/*t.lineNr()*/ 0, "error in regular expression"));
} }
// Do not replace an already defined type validator, this won't work with pdbx_v40 // Do not replace an already defined type validator, this won't work with pdbx_v40
...@@ -482,11 +483,12 @@ class dictionary_parser : public parser_t<> ...@@ -482,11 +483,12 @@ class dictionary_parser : public parser_t<>
// -------------------------------------------------------------------- // --------------------------------------------------------------------
Validator parse_dictionary(std::string_view name, std::istream &is); Validator parse_dictionary(std::string_view name, std::istream &is)
{ {
Validator result(name); Validator result(name);
dictionary_parser p(v, is, file{}); file f;
dictionary_parser p(result, is, f);
p.load_dictionary(); p.load_dictionary();
return result; return result;
......
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cif++/v2/row.hpp>
namespace cif::v2
{
std::string_view item_handle::text() const
{
for (auto iv = m_row_handle.m_row->m_head; iv != nullptr; iv = iv->m_next)
{
if (iv->m_column_ix == m_column)
return iv->text();
}
return {};
}
}
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cassert>
#include <iostream>
#include <map>
#include <regex>
#include <stack>
#include <cif++/CifUtils.hpp>
#include <cif++/v2/forward_decl.hpp>
#include <cif++/v2/parser.hpp>
#include <cif++/v2/file.hpp>
namespace cif
{
extern int VERBOSE;
}
namespace cif::v2
{
// --------------------------------------------------------------------
sac_parser::sac_parser(std::istream &is, bool init)
: m_source(is)
{
m_validate = true;
m_line_nr = 1;
m_bol = true;
if (init)
m_lookahead = get_next_token();
}
// get_next_char takes a char from the buffer, or if it is empty
// from the istream. This function also does carriage/linefeed
// translation.
int sac_parser::get_next_char()
{
int result;
if (m_buffer.empty())
result = m_source.get();
else
{
result = m_buffer.top();
m_buffer.pop();
}
// very simple CR/LF translation into LF
if (result == '\r')
{
int lookahead = m_source.get();
if (lookahead != '\n')
m_buffer.push(lookahead);
result = '\n';
}
m_token_value += static_cast<char>(result);
if (result == '\n')
++m_line_nr;
if (VERBOSE >= 6)
{
std::cerr << "get_next_char => ";
if (iscntrl(result) or not isprint(result))
std::cerr << int(result) << std::endl;
else
std::cerr << char(result) << std::endl;
}
return result;
}
void sac_parser::retract()
{
assert(not m_token_value.empty());
char ch = m_token_value.back();
if (ch == '\n')
--m_line_nr;
m_buffer.push(ch);
m_token_value.pop_back();
}
int sac_parser::restart(int start)
{
int result = 0;
while (not m_token_value.empty())
retract();
switch (start)
{
case State::Start:
result = State::Float;
break;
case State::Float:
result = State::Int;
break;
case State::Int:
result = State::Value;
break;
default:
error("Invalid state in SacParser");
}
m_bol = false;
return result;
}
sac_parser::CIFToken sac_parser::get_next_token()
{
const auto kEOF = std::char_traits<char>::eof();
CIFToken result = CIFToken::Unknown;
int quoteChar = 0;
int state = State::Start, start = State::Start;
m_bol = false;
m_token_value.clear();
mTokenType = CIFValue::Unknown;
while (result == CIFToken::Unknown)
{
auto ch = get_next_char();
switch (state)
{
case State::Start:
if (ch == kEOF)
result = CIFToken::Eof;
else if (ch == '\n')
{
m_bol = true;
state = State::White;
}
else if (ch == ' ' or ch == '\t')
state = State::White;
else if (ch == '#')
state = State::Comment;
else if (ch == '_')
state = State::Tag;
else if (ch == ';' and m_bol)
state = State::TextField;
else if (ch == '\'' or ch == '"')
{
quoteChar = ch;
state = State::QuotedString;
}
else
state = start = restart(start);
break;
case State::White:
if (ch == kEOF)
result = CIFToken::Eof;
else if (not isspace(ch))
{
state = State::Start;
retract();
m_token_value.clear();
}
else
m_bol = (ch == '\n');
break;
case State::Comment:
if (ch == '\n')
{
state = State::Start;
m_bol = true;
m_token_value.clear();
}
else if (ch == kEOF)
result = CIFToken::Eof;
else if (not is_any_print(ch))
error("invalid character in comment");
break;
case State::TextField:
if (ch == '\n')
state = State::TextField + 1;
else if (ch == kEOF)
error("unterminated textfield");
else if (not is_any_print(ch))
warning("invalid character in text field '" + std::string({static_cast<char>(ch)}) + "' (" + std::to_string((int)ch) + ")");
break;
case State::TextField + 1:
if (is_text_lead(ch) or ch == ' ' or ch == '\t')
state = State::TextField;
else if (ch == ';')
{
assert(m_token_value.length() >= 2);
m_token_value = m_token_value.substr(1, m_token_value.length() - 3);
mTokenType = CIFValue::TextField;
result = CIFToken::Value;
}
else if (ch == kEOF)
error("unterminated textfield");
else if (ch != '\n')
error("invalid character in text field");
break;
case State::QuotedString:
if (ch == kEOF)
error("unterminated quoted string");
else if (ch == quoteChar)
state = State::QuotedStringQuote;
else if (not is_any_print(ch))
warning("invalid character in quoted string: '" + std::string({static_cast<char>(ch)}) + '\'');
break;
case State::QuotedStringQuote:
if (is_white(ch))
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::String;
if (m_token_value.length() < 2)
error("Invalid quoted string token");
m_token_value = m_token_value.substr(1, m_token_value.length() - 2);
}
else if (ch == quoteChar)
;
else if (is_any_print(ch))
state = State::QuotedString;
else if (ch == kEOF)
error("unterminated quoted string");
else
error("invalid character in quoted string");
break;
case State::Tag:
if (not is_non_blank(ch))
{
retract();
result = CIFToken::Tag;
}
break;
case State::Float:
if (ch == '+' or ch == '-')
{
state = State::Float + 1;
}
else if (isdigit(ch))
state = State::Float + 1;
else
state = start = restart(start);
break;
case State::Float + 1:
// if (ch == '(') // numeric???
// mState = State::NumericSuffix;
// else
if (ch == '.')
state = State::Float + 2;
else if (tolower(ch) == 'e')
state = State::Float + 3;
else if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Int;
}
else
state = start = restart(start);
break;
// parsed '.'
case State::Float + 2:
if (tolower(ch) == 'e')
state = State::Float + 3;
else if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Float;
}
else
state = start = restart(start);
break;
// parsed 'e'
case State::Float + 3:
if (ch == '-' or ch == '+')
state = State::Float + 4;
else if (isdigit(ch))
state = State::Float + 5;
else
state = start = restart(start);
break;
case State::Float + 4:
if (isdigit(ch))
state = State::Float + 5;
else
state = start = restart(start);
break;
case State::Float + 5:
if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Float;
}
else
state = start = restart(start);
break;
case State::Int:
if (isdigit(ch) or ch == '+' or ch == '-')
state = State::Int + 1;
else
state = start = restart(start);
break;
case State::Int + 1:
if (is_white(ch) or ch == kEOF)
{
retract();
result = CIFToken::Value;
mTokenType = CIFValue::Int;
}
else
state = start = restart(start);
break;
case State::Value:
if (ch == '_')
{
std::string s = toLowerCopy(m_token_value);
if (s == "global_")
result = CIFToken::GLOBAL;
else if (s == "stop_")
result = CIFToken::STOP;
else if (s == "loop_")
result = CIFToken::LOOP;
else if (s == "data_")
{
state = State::DATA;
continue;
}
else if (s == "save_")
{
state = State::SAVE;
continue;
}
}
if (result == CIFToken::Unknown and not is_non_blank(ch))
{
retract();
result = CIFToken::Value;
if (m_token_value == ".")
mTokenType = CIFValue::Inapplicable;
else if (m_token_value == "?")
{
mTokenType = CIFValue::Unknown;
m_token_value.clear();
}
}
break;
case State::DATA:
case State::SAVE:
if (not is_non_blank(ch))
{
retract();
if (state == State::DATA)
result = CIFToken::DATA;
else
result = CIFToken::SAVE;
m_token_value.erase(m_token_value.begin(), m_token_value.begin() + 5);
}
break;
default:
assert(false);
error("Invalid state in get_next_token");
break;
}
}
if (VERBOSE >= 5)
{
std::cerr << get_token_name(result);
if (mTokenType != CIFValue::Unknown)
std::cerr << ' ' << get_value_name(mTokenType);
if (result != CIFToken::Eof)
std::cerr << " " << std::quoted(m_token_value);
std::cerr << std::endl;
}
return result;
}
void sac_parser::match(CIFToken token)
{
if (m_lookahead != token)
error(std::string("Unexpected token, expected ") + get_token_name(token) + " but found " + get_token_name(m_lookahead));
m_lookahead = get_next_token();
}
bool sac_parser::parse_single_datablock(const std::string &datablock)
{
// first locate the start, as fast as we can
auto &sb = *m_source.rdbuf();
enum
{
start,
comment,
string,
string_quote,
qstring,
data
} state = start;
int quote = 0;
bool bol = true;
std::string dblk = "data_" + datablock;
std::string::size_type si = 0;
bool found = false;
for (auto ch = sb.sbumpc(); not found and ch != std::streambuf::traits_type::eof(); ch = sb.sbumpc())
{
switch (state)
{
case start:
switch (ch)
{
case '#': state = comment; break;
case 'd':
case 'D':
state = data;
si = 1;
break;
case '\'':
case '"':
state = string;
quote = ch;
break;
case ';':
if (bol)
state = qstring;
break;
}
break;
case comment:
if (ch == '\n')
state = start;
break;
case string:
if (ch == quote)
state = string_quote;
break;
case string_quote:
if (std::isspace(ch))
state = start;
else
state = string;
break;
case qstring:
if (ch == ';' and bol)
state = start;
break;
case data:
if (isspace(ch) and dblk[si] == 0)
found = true;
else if (dblk[si++] != ch)
state = start;
break;
}
bol = (ch == '\n');
}
if (found)
{
produce_datablock(datablock);
m_lookahead = get_next_token();
parse_datablock();
}
return found;
}
sac_parser::datablock_index sac_parser::index_datablocks()
{
datablock_index index;
// first locate the start, as fast as we can
auto &sb = *m_source.rdbuf();
enum
{
start,
comment,
string,
string_quote,
qstring,
data,
data_name
} state = start;
int quote = 0;
bool bol = true;
const char dblk[] = "data_";
std::string::size_type si = 0;
std::string datablock;
for (auto ch = sb.sbumpc(); ch != std::streambuf::traits_type::eof(); ch = sb.sbumpc())
{
switch (state)
{
case start:
switch (ch)
{
case '#': state = comment; break;
case 'd':
case 'D':
state = data;
si = 1;
break;
case '\'':
case '"':
state = string;
quote = ch;
break;
case ';':
if (bol)
state = qstring;
break;
}
break;
case comment:
if (ch == '\n')
state = start;
break;
case string:
if (ch == quote)
state = string_quote;
break;
case string_quote:
if (std::isspace(ch))
state = start;
else
state = string;
break;
case qstring:
if (ch == ';' and bol)
state = start;
break;
case data:
if (dblk[si] == 0 and is_non_blank(ch))
{
datablock = {static_cast<char>(ch)};
state = data_name;
}
else if (dblk[si++] != ch)
state = start;
break;
case data_name:
if (is_non_blank(ch))
datablock.insert(datablock.end(), char(ch));
else if (isspace(ch))
{
if (not datablock.empty())
index[datablock] = m_source.tellg();
state = start;
}
else
state = start;
break;
}
bol = (ch == '\n');
}
return index;
}
bool sac_parser::parse_single_datablock(const std::string &datablock, const datablock_index &index)
{
bool result = false;
auto i = index.find(datablock);
if (i != index.end())
{
m_source.seekg(i->second);
produce_datablock(datablock);
m_lookahead = get_next_token();
parse_datablock();
result = true;
}
return result;
}
void sac_parser::parse_file()
{
while (m_lookahead != CIFToken::Eof)
{
switch (m_lookahead)
{
case CIFToken::GLOBAL:
parse_global();
break;
case CIFToken::DATA:
produce_datablock(m_token_value);
match(CIFToken::DATA);
parse_datablock();
break;
default:
error("This file does not seem to be an mmCIF file");
break;
}
}
}
void sac_parser::parse_global()
{
match(CIFToken::GLOBAL);
while (m_lookahead == CIFToken::Tag)
{
match(CIFToken::Tag);
match(CIFToken::Value);
}
}
void sac_parser::parse_datablock()
{
std::string cat;
while (m_lookahead == CIFToken::LOOP or m_lookahead == CIFToken::Tag or m_lookahead == CIFToken::SAVE)
{
switch (m_lookahead)
{
case CIFToken::LOOP:
{
cat.clear(); // should start a new category
match(CIFToken::LOOP);
std::vector<std::string> tags;
while (m_lookahead == CIFToken::Tag)
{
std::string catName, itemName;
std::tie(catName, itemName) = splitTagName(m_token_value);
if (cat.empty())
{
produce_category(catName);
cat = catName;
}
else if (not iequals(cat, catName))
error("inconsistent categories in loop_");
tags.push_back(itemName);
match(CIFToken::Tag);
}
while (m_lookahead == CIFToken::Value)
{
produce_row();
for (auto tag : tags)
{
produce_item(cat, tag, m_token_value);
match(CIFToken::Value);
}
}
cat.clear();
break;
}
case CIFToken::Tag:
{
std::string catName, itemName;
std::tie(catName, itemName) = splitTagName(m_token_value);
if (not iequals(cat, catName))
{
produce_category(catName);
cat = catName;
produce_row();
}
match(CIFToken::Tag);
produce_item(cat, itemName, m_token_value);
match(CIFToken::Value);
break;
}
case CIFToken::SAVE:
parse_save_frame();
break;
default:
assert(false);
break;
}
}
}
void sac_parser::parse_save_frame()
{
error("A regular CIF file should not contain a save frame");
}
// --------------------------------------------------------------------
void parser::produce_datablock(const std::string &name)
{
const auto &[iter, ignore] = m_file.emplace(name);
m_datablock = &(*iter);
}
void parser::produce_category(const std::string &name)
{
if (VERBOSE >= 4)
std::cerr << "producing category " << name << std::endl;
const auto &[cat, ignore] = m_datablock->emplace(name);
m_category = &*cat;
}
void parser::produce_row()
{
if (VERBOSE >= 4)
std::cerr << "producing row for category " << m_category->name() << std::endl;
m_category->emplace({});
m_row = m_category->back();
// m_row.lineNr(m_line_nr);
}
void parser::produce_item(const std::string &category, const std::string &item, const std::string &value)
{
if (VERBOSE >= 4)
std::cerr << "producing _" << category << '.' << item << " -> " << value << std::endl;
if (not iequals(category, m_category->name()))
error("inconsistent categories in loop_");
m_row[item] = m_token_value;
}
} // namespace cif::v2
\ No newline at end of file
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 NKI/AVL, Netherlands Cancer Institute
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <cif++/v2/category.hpp>
namespace cif::v2
{
void row_handle::assign(size_t column, std::string_view value, bool updateLinked, bool validate)
{
m_category->update_value(m_row, column, value, updateLinked, validate);
}
uint16_t row_handle::get_column_ix(std::string_view name) const
{
return m_category->get_column_ix(name);
}
uint16_t row_handle::add_column(std::string_view name)
{
return m_category->add_column(name);
}
} // namespace cif::v2
\ No newline at end of file
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <cassert>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
...@@ -217,16 +218,12 @@ const ValidateItem *ValidateCategory::getValidatorForItem(std::string_view tag) ...@@ -217,16 +218,12 @@ const ValidateItem *ValidateCategory::getValidatorForItem(std::string_view tag)
// -------------------------------------------------------------------- // --------------------------------------------------------------------
Validator::Validator(std::string_view name, std::istream &is) // Validator::Validator(std::string_view name, std::istream &is)
: mName(name) // : mName(name)
{ // {
DictParser p(*this, is); // DictParser p(*this, is);
p.loadDictionary(); // p.loadDictionary();
} // }
Validator::~Validator()
{
}
void Validator::addTypeValidator(ValidateType &&v) void Validator::addTypeValidator(ValidateType &&v)
{ {
...@@ -343,7 +340,7 @@ std::vector<const ValidateLink *> Validator::getLinksForChild(std::string_view c ...@@ -343,7 +340,7 @@ std::vector<const ValidateLink *> Validator::getLinksForChild(std::string_view c
void Validator::reportError(const std::string &msg, bool fatal) const void Validator::reportError(const std::string &msg, bool fatal) const
{ {
if (mStrict or fatal) if (m_strict or fatal)
throw ValidationError(msg); throw ValidationError(msg);
else if (VERBOSE > 0) else if (VERBOSE > 0)
std::cerr << msg << std::endl; std::cerr << msg << std::endl;
...@@ -357,7 +354,7 @@ const Validator &ValidatorFactory::operator[](std::string_view dictionary_name) ...@@ -357,7 +354,7 @@ const Validator &ValidatorFactory::operator[](std::string_view dictionary_name)
for (auto &validator : mValidators) for (auto &validator : mValidators)
{ {
if (iequals(validator.m_name, dictionary_name)) if (iequals(validator.name(), dictionary_name))
return validator; return validator;
} }
...@@ -422,12 +419,12 @@ const Validator &ValidatorFactory::operator[](std::string_view dictionary_name) ...@@ -422,12 +419,12 @@ const Validator &ValidatorFactory::operator[](std::string_view dictionary_name)
throw std::runtime_error("Dictionary not found or defined (" + dictionary.string() + ")"); throw std::runtime_error("Dictionary not found or defined (" + dictionary.string() + ")");
} }
assert(iequals(mValidators.back().m_name, dictionary_name)); assert(iequals(mValidators.back().name(), dictionary_name));
return mValidators.back(); return mValidators.back();
} }
void ValiatorFactory::construct_validator(std::string_view name, std::istream &is) void ValidatorFactory::construct_validator(std::string_view name, std::istream &is)
{ {
mValidators.emplace_back(parse_dictionary(name, is)); mValidators.emplace_back(parse_dictionary(name, is));
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment