21#include <absl/base/attributes.h>
22#include <absl/base/optimization.h>
23#include <absl/container/btree_map.h>
24#include <absl/log/absl_check.h>
25#include <absl/strings/str_cat.h>
26#include <absl/strings/str_format.h>
27#include <boost/range/iterator_range.hpp>
34 enum class CifToken : std::uint32_t {
53 kSimpleValue = kValue,
54 kQuotedValue = kValue | kIsQuoted,
57 constexpr bool is_value_token(CifToken token) {
58 return static_cast<bool>(token & CifToken::kValue);
61 extern std::ostream &operator<<(std::ostream &os, CifToken type);
63 using SIter = std::string::const_iterator;
67 explicit CifLexer(std::istream &is): is_(&is) {
68 static_cast<void>(advance_line<true>());
71 std::pair<std::string_view, CifToken> next();
75 size_t row()
const {
return row_; }
76 size_t col()
const {
return it_ - begin_ + 1; }
78 SIter begin()
const {
return begin_; }
80 SIter p()
const {
return it_; }
83 ABSL_DCHECK(it_ < end());
87 SIter end()
const {
return end_; }
89 std::string_view line()
const {
return line_; }
91 template <
bool kSkipEmpty>
92 ABSL_MUST_USE_RESULT
bool advance_line() {
94 if (!std::getline(*is_, line_)) {
101 begin_ = it_ = line_.begin();
103 }
while (kSkipEmpty && begin_ == end_);
110 std::pair<std::string_view, CifToken> produce(std::string_view data,
111 CifToken type, SIter after) {
113 return { data, type };
116 template <
class... Args, std::enable_if_t<
sizeof...(Args) != 0,
int> = 0>
117 std::pair<std::string_view, CifToken> error(Args &&...args) {
120 std::string err = absl::StrCat(std::forward<Args>(args)...);
121 buf_ = std::move(err);
122 return { buf_, CifToken::kError };
125 static std::pair<std::string_view, CifToken> done() {
126 return { {}, CifToken::kEOF };
130 Nonnull<std::istream *> is_;
137 std::size_t row_ = 0;
142 enum class Type : std::uint32_t {
149 kFloatNonfinite = 1U << 2,
152 kInapplicable = 1U << 31,
155 CifValue(std::string_view value, internal::CifToken type): value_(value) {
156 if (type == internal::CifToken::kQuotedValue) {
157 type_ = Type::kString;
161 ABSL_DCHECK_EQ(type, internal::CifToken::kSimpleValue);
165 type_ = Type::kUnknown;
166 }
else if (value ==
".") {
168 type_ = Type::kInapplicable;
175 static CifValue
generic(T &&value) {
176 return CifValue(std::forward<T>(value), Type::kGeneric);
181 static CifValue string(T &&value) {
182 return CifValue(std::forward<T>(value), Type::kString);
185 static CifValue unknown() {
return CifValue(
"", Type::kUnknown); }
187 static CifValue inapplicable() {
return CifValue(
"", Type::kInapplicable); }
189 static CifValue null(
bool is_unk) {
190 return CifValue(
"", is_unk ? Type::kUnknown : Type::kInapplicable);
194 static CifValue float_nonfinite(T &&repr) {
195 return CifValue(std::forward<T>(repr), Type::kFloatNonfinite);
198 std::string_view operator*()
const {
return value_; }
199 std::string_view value()
const {
return value_; }
201 const std::string *operator->()
const {
return &value_; }
203 Type type()
const {
return type_; }
205 constexpr bool is_null()
const {
206 return static_cast<bool>(type_ & (Type::kUnknown | Type::kInapplicable));
209 constexpr operator bool()
const {
return !is_null(); }
211 bool operator==(std::string_view other)
const;
215 CifValue(T &&value, Type type)
216 : value_(std::forward<T>(value)), type_(type) { }
219 Type type_ = Type::kGeneric;
222 extern std::ostream &operator<<(std::ostream &os,
const CifValue &value);
224 class CifTableColumn;
228 CifTable() =
default;
230 const std::vector<std::string> &keys()
const {
return keys_; }
231 void add_key(std::string_view key) { keys_.push_back(std::string { key }); }
233 const std::vector<std::vector<CifValue>> &data()
const {
return rows_; }
234 void add_data(CifValue &&value);
236 const std::vector<CifValue> &operator[](
size_t i)
const {
return rows_[i]; }
237 size_t size()
const {
return rows_.size(); }
239 auto begin()
const {
return rows_.begin(); }
240 auto end()
const {
return rows_.end(); }
242 const std::vector<CifValue> &row(
size_t i)
const {
return rows_[i]; }
243 size_t rows()
const {
return rows_.size(); }
245 CifTableColumn col(
size_t i)
const;
246 size_t cols()
const {
return keys_.size(); }
248 bool empty()
const {
return keys_.empty() || rows_.empty(); }
250 std::string validate()
const;
253 std::vector<std::string> keys_;
254 std::vector<std::vector<CifValue>> rows_;
257 class CifTableColumn {
259 CifTableColumn(Nonnull<const CifTable *> table,
size_t col)
260 : table_(table), col_(col) { }
262 std::string_view key()
const {
return table_->keys()[col_]; }
263 size_t idx()
const {
return col_; }
265 const CifValue &operator[](
size_t i)
const {
return table_->row(i)[col_]; }
266 size_t size()
const {
return table_->rows(); }
269 Nonnull<const CifTable *> table_;
273 inline CifTableColumn CifTable::col(
size_t i)
const {
279 enum class Type :
int {
280 kGlobal =
static_cast<int>(CifToken::kGlobal),
281 kData =
static_cast<int>(CifToken::kData),
282 kSave =
static_cast<int>(CifToken::kSave),
285 CifFrame(std::vector<CifTable> &&tables, std::string &&name);
287 CifFrame() noexcept = default;
288 CifFrame(CifFrame &&) noexcept = default;
289 CifFrame &operator=(CifFrame &&) noexcept = default;
290 ~CifFrame() = default;
293 CifFrame(const CifFrame &) = delete;
294 CifFrame &operator=(const CifFrame &) = delete;
296 std::string_view name()
const {
return name_; }
298 const std::vector<CifTable> &tables()
const {
return tables_; }
300 const CifTable &operator[](
size_t i)
const {
return tables_[i]; }
301 size_t size()
const {
return tables_.size(); }
303 auto begin()
const {
return tables_.begin(); }
304 auto end()
const {
return tables_.end(); }
306 size_t total_cols()
const {
return index_.size(); }
308 auto prefix_search(std::string_view prefix)
const {
309 return boost::make_iterator_range(index_.lower_bound(prefix),
313 std::pair<int, int> find(std::string_view key)
const {
314 auto it = index_.find(key);
315 if (it == index_.end())
320 CifTableColumn get(
size_t tbl,
size_t col)
const {
321 return tables_[tbl].col(col);
324 std::string validate(
bool recursive =
true)
const;
328 std::vector<CifTable> tables_;
329 absl::btree_map<std::string_view, std::pair<int, int>> index_;
334 enum class Type :
int {
335 kEOF =
static_cast<int>(CifToken::kEOF),
336 kError =
static_cast<int>(CifToken::kError),
337 kGlobal =
static_cast<int>(CifToken::kGlobal),
338 kData =
static_cast<int>(CifToken::kData),
341 CifBlock(CifFrame &&frame, std::vector<CifFrame> &&save, Type type) noexcept
342 : frame_(std::move(frame)), save_(std::move(save)), type_(type) {
346 static CifBlock eof() noexcept {
return {}; }
348 static CifBlock error(std::string_view reason) {
return { reason }; }
349 std::string_view error_msg()
const {
350 ABSL_DCHECK(type_ == Type::kError);
351 return frame_.name();
354 std::string_view name()
const {
355 ABSL_DCHECK(type_ != Type::kError);
356 return frame_.name();
359 const CifFrame &data()
const {
return frame_; }
361 const std::vector<CifFrame> &save_frames()
const {
return save_; }
363 Type type()
const {
return type_; }
365 std::string validate(
bool recursive =
true)
const;
367 constexpr operator bool()
const {
368 return type_ != Type::kEOF && type_ != Type::kError;
372 CifBlock() noexcept: type_(Type::kEOF) { }
374 CifBlock(std::string_view error_msg)
375 : frame_({}, std::string { error_msg }), type_(Type::kError) { }
378 std::vector<CifFrame> save_;
393 internal::CifBlock error(std::string_view reason);
396 internal::CifLexer lexer_;
399 internal::CifBlock::Type block_ = internal::CifBlock::Type::kEOF;
407inline internal::CifValue
cif_value(std::string_view value) {
408 return internal::CifValue::string(value);
421template <
class T, std::enable_if_t<std::is_same_v<T,
bool>,
int> = 0>
422internal::CifValue
cif_value(T value,
bool short_form =
false) {
423 return internal::CifValue::generic(value ? (short_form ?
"y" :
"yes")
424 : (short_form ?
"n" :
"no"));
435 std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>,
int> = 0>
438 return internal::CifValue::generic(absl::StrCat(value));
440 return internal::CifValue::generic(
441 absl::StrFormat(
"%0*d", width,
static_cast<std::int64_t
>(value)));
445 extern CifValue cif_float_nonfinite(
double value,
bool coerce_nonfinite,
465template <
class T, std::enable_if_t<std::is_
floating_po
int_v<T>,
int> = 0>
466internal::CifValue
cif_value(T value,
int precision = -1,
467 bool coerce_nonfinite =
false,
468 bool is_unk =
true) {
469 if (ABSL_PREDICT_FALSE(!std::isfinite(value))) {
470 return internal::cif_float_nonfinite(
static_cast<double>(value),
471 coerce_nonfinite, is_unk);
475 return internal::CifValue::generic(absl::StrCat(value));
477 return internal::CifValue::generic(
478 absl::StrFormat(
"%.*f", precision,
static_cast<double>(value)));
490 std::enable_if_t<!std::is_arithmetic_v<std::decay_t<T>>
491 && !std::is_convertible_v<T, std::string_view>,
494 return internal::CifValue::string(absl::StrCat(std::forward<T>(value)));
498 enum class CifValueKind { kError, kInline, kTextField };
500 extern CifValueKind write_cif_value(std::string &out,
501 const internal::CifValue &value);
537 internal::CifFrame::Type type = internal::CifFrame::Type::kData,
561 enum class CifGlobalCtx {
566 extern std::pair<std::string_view, CifToken>
567 parse_data(CifGlobalCtx ctx, std::vector<CifTable> &tables, CifLexer &lexer,
568 std::string_view name);
570 extern CifBlock next_block(CifParser &parser, CifLexer &lexer,
571 std::string &next_name,
572 internal::CifBlock::Type &next_block);
internal::CifBlock next()
Parse the next block in the CIF file.
CifParser(std::istream &is)
bool write_cif_table(std::string &out, const internal::CifTable &table, bool align=false)
Serialize a CIF table (key-value pairs or a loop_), appending to out.
bool write_cif_frame(std::string &out, const internal::CifFrame &frame, internal::CifFrame::Type type=internal::CifFrame::Type::kData, bool align=false)
Serialize a CIF frame, appending to out.
@ kUnknown
Definition molecule.h:588
internal::CifValue cif_value(std::string_view value)
Create a string CIF value from text.
Definition cif.h:407
constexpr bool operator==(const Isotope &lhs, const Isotope &rhs) noexcept
Definition element.h:34
bool write_cif_block(std::string &out, const internal::CifBlock &block, bool align=false)
Serialize a CIF block (a data or global block plus its save frames), appending to out.