ampsci
High-precision calculations for one- and two-valence atomic systems
InputBlock.hpp
1#pragma once
2#include "fmt/color.hpp"
3#include "qip/String.hpp" //for case insensitive
4#include <algorithm>
5#include <array>
6#include <chrono>
7#include <fstream>
8#include <iostream>
9#include <istream>
10#include <optional>
11#include <sstream>
12#include <string>
13#include <string_view>
14#include <vector>
15
16namespace IO {
17
18//! Prints unkown option warning, with suggested alternative
19inline void unkown_option(std::string_view test_string,
20 const std::vector<std::string> &list) {
21 fmt2::warning();
22 std::cout << ": Unknown option: " << test_string << "\n";
23 if (!list.empty())
24 std::cout << "Did you mean: " << qip::ci_closest_match(test_string, list)
25 << " ?\n ";
26}
27
28//==============================================================================
29//! Removes all white space (space, tab, newline), except for those in quotes
30inline std::string removeSpaces(std::string str);
31
32//! Removes all quote marks
33inline std::string removeQuoteMarks(std::string str);
34
35//! Removes all c++ style block comments from a string
36inline void removeBlockComments(std::string &input);
37
38//! Removes all c++ style comments from a string (block and line)
39inline std::string removeComments(const std::string &input);
40
41//! Expands "#include" files
42inline std::string expandIncludes(std::string input);
43
44//! Parses a string to type T by stringstream
45template <typename T>
46inline T parse_str_to_T(const std::string &value_as_str);
47
48//! Parses entire file into string. Note: v. inefficient
49inline std::string file_to_string(const std::istream &file);
50
51//! Class to determine if a class template in vector
52template <typename T>
53struct IsVector {
54 constexpr static bool v = false;
55 using t = T;
56};
57template <typename T>
58struct IsVector<std::vector<T>> {
59 constexpr static bool v = true;
60 // nb: returns conatined type of vector
61 using t = T;
62};
63// e.g.:
64// std::cout << std::boolalpha;
65// std::cout << IO::IsVector<int>::v << "\n";
66// std::cout << IO::IsVector<std::vector<int>>::v << "\n";
67// std::cout << IO::IsVector<std::vector<double>>::v << "\n";
68
69template <typename T>
70struct IsArray {
71 constexpr static bool v = false;
72 using t = T;
73 static constexpr std::size_t size = 0;
74};
75template <typename T, std::size_t N>
76struct IsArray<std::array<T, N>> {
77 constexpr static bool v = true;
78 // nb: returns conatined type of array
79 using t = T;
80 static constexpr std::size_t size = N;
81};
82
83//! Prints a line of 'c' characters (dflt '*'), num chars long (dflt 80) to
84//! cout
85inline void print_line(const char c = '*', const int num = 80) {
86 for (int i = 0; i < num; i++)
87 std::cout << c;
88 std::cout << "\n";
89}
90
91//==============================================================================
92inline std::string time_date() {
93 const auto now =
94 std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
95 char buffer[30];
96 std::strftime(buffer, 30, "%F %T", localtime(&now));
97 return buffer;
98}
99inline std::string date() {
100 const auto now =
101 std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
102 char buffer[30];
103 std::strftime(buffer, 30, "%F", localtime(&now));
104 return buffer;
105}
106inline std::string time() {
107 const auto now =
108 std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
109 char buffer[30];
110 std::strftime(buffer, 30, "%T", localtime(&now));
111 return buffer;
112}
113
114//==============================================================================
115//! Simple struct; holds key-value pair, both strings. == compares key
116struct Option {
117 std::string key;
118 std::string value_str;
119
120 friend bool operator==(Option option, std::string_view tkey) {
121 // return option.key == tkey;
122 return qip::ci_wc_compare(tkey, option.key);
123 }
124 friend bool operator==(std::string_view tkey, Option option) {
125 return option == tkey;
126 }
127 friend bool operator!=(Option option, std::string_view tkey) {
128 return !(option == tkey);
129 }
130 friend bool operator!=(std::string_view tkey, Option option) {
131 return !(option == tkey);
132 }
133};
134
135//==============================================================================
136//! Holds list of Options, and a list of other InputBlocks. Can be initialised
137//! with a list of options, with a string, or from a file (ifstream).
138//! Format for input is, e.g.,:
139/*!
140 BlockName1{
141 option1=value1;
142 option2=value2;
143 InnerBlock{
144 option1=v3;
145 }
146 }
147
148 Note: comparison for block/option names is case insensitive!
149*/
150// nb: I sepparate the function implementations below (in the header file) and
151// mark them as inline. This is for readability only, and ensures this file
152// works as a single-file header-only
154private:
155 std::string m_name{};
156 std::vector<Option> m_options{};
157 std::vector<InputBlock> m_blocks{};
158
159public:
160 //! Default constructor: name will be blank
162
163 //! Construct from literal list of 'Options' (see Option struct)
164 InputBlock(std::string_view name, std::initializer_list<Option> options = {})
165 : m_name(name), m_options(options) {}
166
167 //! Construct from a string with the correct Block{option=value;} format
168 InputBlock(std::string_view name, const std::string &string_input)
169 : m_name(name) {
170 add(string_input);
171 }
172
173 //! Construct from plain text file, in Block{option=value;} format
174 InputBlock(std::string_view name, const std::istream &file) : m_name(name) {
175 add(file_to_string(file));
176 }
177
178 //! Add a new InputBlock (merge: will be merged with existing if names
179 //! match)
180 inline void add(InputBlock block, bool merge = false);
181 inline void merge(InputBlock block) { add(block, true); }
182 //! Adds a new option to end of list
183 inline void add(Option option);
184 inline void add(const std::vector<Option> &options);
185 //! Adds options/inputBlocks by parsing a string
186 inline void add(const std::string &string, bool merge = false);
187 inline void merge(const std::string &string) { add(string, true); }
188
189 std::string_view name() const { return m_name; }
190 //! Return const reference to list of options
191 const std::vector<Option> &options() const { return m_options; }
192 //! Return const reference to list of blocks
193 const std::vector<InputBlock> &blocks() const { return m_blocks; }
194
195 //! Comparison of blocks compares the 'name'
196 friend inline bool operator==(InputBlock block, std::string_view name);
197 friend inline bool operator==(std::string_view name, InputBlock block);
198 friend inline bool operator!=(InputBlock block, std::string_view name);
199 friend inline bool operator!=(std::string_view name, InputBlock block);
200
201 //! If 'key' exists in the options, returns value. Else, returns
202 //! default_value. Note: If two keys with same name, will use the later
203 template <typename T>
204 T get(std::string_view key, T default_value) const;
205
206 //! Returns optional value. Contains value if key exists; empty otherwise.
207 //! Note: If two keys with same name, will use the later
208 template <typename T = std::string>
209 std::optional<T> get(std::string_view key) const;
210
211 //! Check is option is present (even if not set) in current block.
212 bool has_option(std::string_view key) const {
213 const auto option = std::find(m_options.crbegin(), m_options.crend(), key);
214 return !(option == m_options.crend());
215 }
216
217 //! Check if option is present AND has been set
218 bool option_is_set(std::string_view key) const {
219 return !(get(key) == std::nullopt);
220 }
221
222 //! Get value from set of nested blocks. .get({block1,block2},option)
223 template <typename T>
224 T get(std::initializer_list<std::string> blocks, std::string_view key,
225 T default_value) const;
226 //! As above, but without default value
227 template <typename T>
228 std::optional<T> get(std::initializer_list<std::string> blocks,
229 std::string_view key) const;
230
231 //! Returns optional InputBlock. Contains InputBlock if block of given name
232 //! exists; empty otherwise.
233 inline std::optional<InputBlock> getBlock(std::string_view name) const;
234 inline std::optional<InputBlock>
235 getBlock(std::initializer_list<std::string> blocks,
236 std::string_view name) const;
237
238 //! If block is present, returns a copy of it. If not, returns empty block
239 inline InputBlock get_block(std::string_view name) const {
240 auto temp = getBlock(name);
241 if (temp)
242 return *temp;
243 return InputBlock{};
244 }
245
246 //! Checks if block 'name' is present in current block
247 bool has_block(std::string_view name) const {
248 auto temp = getBlock(name);
249 return temp != std::nullopt;
250 }
251 //! Checks if block 'name' is present in nesteded block
252 bool has_block(std::initializer_list<std::string> blocks,
253 std::string_view name) const {
254 auto temp = getBlock(blocks, name);
255 return temp != std::nullopt;
256 }
257
258 //! Get an 'Option' (kay, value) - rarely needed
259 inline std::optional<Option> getOption(std::string_view key) const;
260
261 //! Prints options to screen in user-friendly form. Same form as input
262 //! string. By default prints to cout, but can be given any ostream
263 inline void print(std::ostream &os = std::cout, int indent_depth = 0) const;
264
265 //! Check all the options and blocks in this; if any of them are not present
266 //! in 'list', then there is likely a spelling error in the input => returns
267 //! false, warns user, and prints all options to screen. list is a pair:
268 //! {option, description}. Description allws you to explain what each option
269 //! is - great for 'self-documenting' code
270 //! If print=true - will print all options+descriptions even if all good.
271 inline bool
272 check(std::initializer_list<std::string> blocks,
273 const std::vector<std::pair<std::string, std::string>> &list,
274 bool print = false) const;
275
276 //! Override for when condidering current block
277 inline bool
278 check(const std::vector<std::pair<std::string, std::string>> &list,
279 bool print = false) const {
280 return checkBlock(list, print);
281 }
282
283private:
284 inline bool
285 checkBlock(const std::vector<std::pair<std::string, std::string>> &list,
286 bool print = false) const;
287
288 //! Return a pointer to a block. Allows editing of blocks
289 inline InputBlock *getBlock_ptr(std::string_view name);
290 inline const InputBlock *getBlock_cptr(std::string_view name) const;
291
292 // Allows returning std::vector: comma-separated list input
293 template <typename T>
294 std::optional<std::vector<T>> get_vector(std::string_view key) const;
295
296 // Allows returning std::array: comma-separated list input
297 template <typename T, std::size_t N>
298 std::optional<std::array<T, N>> get_array(std::string_view key) const;
299
300 inline void add_option(std::string_view in_string);
301 inline void add_blocks_from_string(std::string_view string, bool merge);
302 inline void consolidate();
303};
304
305//==============================================================================
306//==============================================================================
307void InputBlock::add(InputBlock block, bool merge) {
308 auto existing_block = getBlock_ptr(block.m_name);
309 if (merge && existing_block) {
310 existing_block->m_options.insert(existing_block->m_options.end(),
311 block.m_options.cbegin(),
312 block.m_options.cend());
313 } else {
314 m_blocks.push_back(block);
315 }
316}
317
318//==============================================================================
319void InputBlock::add(Option option) { m_options.push_back(option); }
320void InputBlock::add(const std::vector<Option> &options) {
321 for (const auto &option : options)
322 m_options.push_back(option);
323}
324//==============================================================================
325void InputBlock::add(const std::string &string, bool merge) {
326
327 add_blocks_from_string(
329 merge);
330}
331
332//==============================================================================
333bool operator==(InputBlock block, std::string_view name) {
334 return qip::ci_wc_compare(name, block.m_name);
335}
336bool operator==(std::string_view name, InputBlock block) {
337 return block == name;
338}
339bool operator!=(InputBlock block, std::string_view name) {
340 return !(block == name);
341}
342bool operator!=(std::string_view name, InputBlock block) {
343 return !(block == name);
344}
345
346//==============================================================================
347template <typename T>
348std::optional<T> InputBlock::get(std::string_view key) const {
349 if constexpr (IsVector<T>::v) {
350 return get_vector<typename IsVector<T>::t>(key);
351 } else if constexpr (IsArray<T>::v) {
352 return get_array<typename IsArray<T>::t, IsArray<T>::size>(key);
353 } else if constexpr (std::is_same_v<T, bool>) {
354 const auto option = std::find(m_options.crbegin(), m_options.crend(), key);
355 if (option == m_options.crend())
356 return std::nullopt;
357 if (qip::ci_wc_compare("default", option->value_str) ||
358 option->value_str == "")
359 return std::nullopt;
360 const auto &str = option->value_str;
361 if (qip::ci_wc_compare("true", str) || qip::ci_wc_compare("yes", str) ||
362 qip::ci_wc_compare("y", str))
363 return true;
364 return false;
365 } else {
366 // Use reverse iterators so that we find _last_ option that matches key
367 // i.e., assume later options override earlier ones.
368 const auto option = std::find(m_options.crbegin(), m_options.crend(), key);
369 if (option == m_options.crend())
370 return std::nullopt;
371 if (qip::ci_wc_compare("default", option->value_str) ||
372 option->value_str == "")
373 return std::nullopt;
374 return parse_str_to_T<T>(option->value_str);
375 }
376}
377
378// special function; allows return of std::vector (for comma-separated list
379// input). Optional of vector is kind of redundant, but is this way so it
380// aligns with the other functions (checks if optional is empty when deciding
381// if should return the default value)
382template <typename T>
383std::optional<std::vector<T>>
384InputBlock::get_vector(std::string_view key) const {
385 // Use reverse iterators so that we find _last_ option that matches key
386 // i.e., assume later options override earlier ones.
387 std::vector<T> out;
388 const auto option = std::find(m_options.crbegin(), m_options.crend(), key);
389 if (option == m_options.crend())
390 return std::nullopt;
391 if (option->value_str == "")
392 return std::nullopt;
393 std::stringstream ss(option->value_str);
394 while (ss.good()) {
395 // note: *very* innefficient
396 std::string substr;
397 std::getline(ss, substr, ',');
398 out.push_back(parse_str_to_T<T>(substr));
399 }
400 return out;
401}
402
403template <typename T, std::size_t N>
404std::optional<std::array<T, N>>
405InputBlock::get_array(std::string_view key) const {
406 // Use reverse iterators so that we find _last_ option that matches key
407 // i.e., assume later options override earlier ones.
408 std::array<T, N> out;
409 const auto option = std::find(m_options.crbegin(), m_options.crend(), key);
410 if (option == m_options.crend())
411 return std::nullopt;
412 if (option->value_str == "")
413 return std::nullopt;
414 std::stringstream ss(option->value_str);
415 std::size_t index = 0;
416 while (ss.good()) {
417 // note: *very* innefficient
418 std::string substr;
419 std::getline(ss, substr, ',');
420 // out.push_back(parse_str_to_T<T>(substr));
421 out.at(index) = parse_str_to_T<T>(substr);
422 ++index;
423 }
424 return out;
425}
426
427template <typename T>
428T InputBlock::get(std::string_view key, T default_value) const {
429 static_assert(!std::is_same_v<T, const char *>,
430 "Cannot use get with const char* - use std::string");
431 return get<T>(key).value_or(default_value);
432}
433
434template <typename T>
435T InputBlock::get(std::initializer_list<std::string> blocks,
436 std::string_view key, T default_value) const {
437 return get<T>(blocks, key).value_or(default_value);
438}
439
440template <typename T>
441std::optional<T> InputBlock::get(std::initializer_list<std::string> blocks,
442 std::string_view key) const {
443 // Find key in nested blocks
444 const InputBlock *pB = this;
445 for (const auto &block : blocks) {
446 pB = pB->getBlock_cptr(block);
447 if (pB == nullptr)
448 return std::nullopt;
449 }
450 return pB->get<T>(key);
451}
452
453//==============================================================================
454std::optional<InputBlock> InputBlock::getBlock(std::string_view name) const {
455 // note: by copy!
456 const auto block = std::find(m_blocks.crbegin(), m_blocks.crend(), name);
457 if (block == m_blocks.crend())
458 return {};
459 return *block;
460}
461
462std::optional<InputBlock>
463InputBlock::getBlock(std::initializer_list<std::string> blocks,
464 std::string_view name) const {
465 // note: by copy!
466 const InputBlock *pB = this;
467 for (const auto &block : blocks) {
468 pB = pB->getBlock_cptr(block);
469 if (pB == nullptr)
470 return std::nullopt;
471 }
472 return pB->getBlock(name);
473}
474
475//==============================================================================
476std::optional<Option> InputBlock::getOption(std::string_view key) const {
477 // Use reverse iterators so that we find _last_ option that matches key
478 // i.e., assume later options override earlier ones.
479 const auto option = std::find(m_options.crbegin(), m_options.crend(), key);
480 if (option != m_options.crend())
481 return *option;
482 return {};
483}
484
485//==============================================================================
486void InputBlock::print(std::ostream &os, int depth) const {
487
488 std::string indent = "";
489 for (int i = 1; i < depth; ++i)
490 indent += " ";
491
492 // Don't print outer-most name
493 if (depth != 0)
494 os << indent << m_name << " { ";
495
496 const auto multi_entry = (!m_blocks.empty() || (m_options.size() > 1));
497
498 if (depth != 0 && multi_entry)
499 os << "\n";
500
501 for (const auto &[key, value] : m_options) {
502 os << (depth != 0 && multi_entry ? indent + " " : "");
503 if (value == "")
504 os << key << ';';
505 else
506 os << key << " = " << value << ';';
507 os << (multi_entry ? '\n' : ' ');
508 }
509
510 for (const auto &block : m_blocks)
511 block.print(os, depth + 1);
512
513 if (depth != 0 && multi_entry)
514 os << indent;
515
516 if (depth != 0)
517 os << "}\n";
518}
519
520//==============================================================================
521bool InputBlock::checkBlock(
522 const std::vector<std::pair<std::string, std::string>> &list,
523 bool print) const {
524 // Check each option NOT each sub block!
525 // For each input option stored, see if it is allowed
526 // "allowed" means appears in list
527 bool all_ok = true;
528 for (const auto &option : m_options) {
529 const auto is_optionQ = [&](const auto &l) {
530 // return option.key == l.first;
531 return qip::ci_wc_compare(l.first, option.key);
532 };
533 const auto bad_option =
534 !std::any_of(list.cbegin(), list.cend(), is_optionQ);
535 const auto help = qip::ci_wc_compare("help", option.key) ? true : false;
536 if (help)
537 print = true;
538 if (bad_option && !help) {
539 all_ok = false;
540 fmt2::styled_print(fg(fmt::color::orange), "\nWARNING\n");
541 std::cout << "Unclear input option in " << m_name << ": " << option.key
542 << " = " << option.value_str << ";\n"
543 << "Option may be ignored!\n"
544 << "Check spelling (or update list of options)\n";
545 // spell-check + nearest suggestion:
546 auto compare_sc = [&option](const auto &s1, const auto &s2) {
547 return qip::ci_Levenstein(s1.first, option.key) <
548 qip::ci_Levenstein(s2.first, option.key);
549 };
550 auto guess = std::min_element(list.cbegin(), list.cend(), compare_sc);
551 if (guess != list.cend()) {
552 std::cout << "\nDid you mean: " << guess->first << " ?\n";
553 }
554 }
555 }
556
557 using namespace std::string_literals;
558 for (const auto &block : m_blocks) {
559 const auto is_blockQ = [&](const auto &b) {
560 return qip::ci_wc_compare(std::string{block.name()} + "{}"s, b.first);
561 };
562 const auto bad_block = !std::any_of(list.cbegin(), list.cend(), is_blockQ);
563 if (bad_block) {
564 all_ok = false;
565 fmt2::styled_print(fg(fmt::color::orange), "\nWARNING\n");
566 std::cout << "Unclear input block within " << m_name << ": "
567 << block.name() << "{}\n"
568 << "Block and containing options may be ignored!\n"
569 << "Check spelling (or update list of options)\n";
570 // spell-check + nearest suggestion:
571 auto compare_sc = [&block](const auto &s1, const auto &s2) {
572 return qip::ci_Levenstein(s1.first, block.name()) <
573 qip::ci_Levenstein(s2.first, block.name());
574 };
575 auto guess = std::min_element(list.cbegin(), list.cend(), compare_sc);
576 if (guess != list.cend()) {
577 std::cout << "\nDid you mean: " << guess->first << " ?\n";
578 }
579 }
580 }
581
582 if (!all_ok || print) {
583 fmt2::styled_print(fg(fmt::color::light_blue),
584 "\n// Available {} options/blocks\n", m_name);
585 fmt2::styled_print(fmt::emphasis::bold, m_name);
586 std::cout << "{\n";
587 std::for_each(list.cbegin(), list.cend(), [](const auto &s) {
588 const auto option_is_block = s.first.back() == '}';
589 const auto leading_spaces = s.first.empty() ? ""s : " ";
590 if (!s.first.empty()) {
591 std::cout << " " << s.first << (option_is_block ? "\n" : ";\n");
592 }
593 if (!s.second.empty()) {
594 fmt2::styled_print(fg(fmt::color::light_blue), "{}\n",
595 qip::wrap(s.second, 60, leading_spaces + " // "));
596 } else {
597 std::cout << "\n";
598 }
599 });
600 std::cout << "}\n\n";
601 }
602 return all_ok;
603}
604
605//! Check one of the sub-blocks
606bool InputBlock::check(
607 std::initializer_list<std::string> blocks,
608 const std::vector<std::pair<std::string, std::string>> &list,
609 bool print) const {
610 // Find key in nested blocks
611 const InputBlock *pB = this;
612 for (const auto &block : blocks) {
613 pB = pB->getBlock_cptr(block);
614 if (pB == nullptr) {
615 // Did not fund nested block... may be fine
616 // Return true, since a missing block is not an issue (or sepparate
617 // issue) We are checking to see if blocks exist that shouldn't
618 return true;
619 }
620 }
621 return pB->check(list, print);
622}
623
624//==============================================================================
625void InputBlock::add_blocks_from_string(std::string_view string, bool merge) {
626
627 // Expects that string has comments and spaces removed already
628
629 auto start = 0ul;
630 while (start < string.length()) {
631
632 // Find the first of either next ';' or open '{'
633 // This is the end of the next input option, or start of block
634 auto end = std::min(string.find(';', start), string.find('{', start));
635 if (end > string.length() || start >= end)
636 break;
637
638 if (string.at(end) == ';') {
639 // end of option:
640
641 this->add_option(string.substr(start, end - start));
642
643 } else {
644 // start of block
645
646 // 'name' directly preceeds "{"
647 const auto block_name = string.substr(start, end - start);
648 start = end + 1;
649
650 // Now, find *matching* close '}' - ensure balanced
651 int depth_count = 1;
652 auto next_start = start; // + 1;
653 while (depth_count != 0) {
654 if (next_start > string.length())
655 break;
656 const auto next_end =
657 std::min(string.find('{', next_start), string.find('}', next_start));
658 if (next_end > string.length())
659 break;
660
661 // count depth of bracket nesting:
662 if (string.at(next_end) == '{')
663 ++depth_count;
664 else
665 --depth_count;
666
667 if (depth_count == 0) {
668 end = next_end;
669 break;
670 }
671 if (depth_count > 100) {
672 std::cerr << "FAIL 271 in InputBlock::add_blocks_from_string: Depth "
673 "error. Check balanced {} in input\n";
674 end = next_end;
675 break;
676 }
677 next_start = next_end + 1;
678 }
679
680 // Add a new block, populate it with string. Recursive, since blocks may
681 // contain blocks
682 auto &block = m_blocks.emplace_back(block_name);
683
684 if (end > start)
685 block.add_blocks_from_string(string.substr(start, end - start), merge);
686 }
687
688 start = end + 1;
689 }
690
691 // Merge duplicated blocks.
692 if (merge)
693 consolidate();
694 // No - want ability to have multiple blocks of same name
695}
696
697//==============================================================================
698void InputBlock::add_option(std::string_view in_string) {
699 const auto pos = in_string.find('=');
700 const auto option = in_string.substr(0, pos);
701 const auto value = pos < in_string.length() ? in_string.substr(pos + 1) : "";
702 m_options.push_back({std::string(option), std::string(value)});
703}
704
705//==============================================================================
706InputBlock *InputBlock::getBlock_ptr(std::string_view name) {
707 auto block = std::find(m_blocks.rbegin(), m_blocks.rend(), name);
708 if (block == m_blocks.rend())
709 return nullptr;
710 return &(*block);
711}
712
713const InputBlock *InputBlock::getBlock_cptr(std::string_view name) const {
714 auto block = std::find(m_blocks.crbegin(), m_blocks.crend(), name);
715 if (block == m_blocks.rend())
716 return nullptr;
717 return &(*block);
718}
719
720//==============================================================================
721void InputBlock::consolidate() {
722 for (auto bl = m_blocks.end() - 1; bl != m_blocks.begin() - 1; --bl) {
723 bl->consolidate();
724 auto bl2 = std::find(m_blocks.begin(), bl, bl->name());
725 if (bl2 != bl) {
726 bl2->m_options.insert(bl2->m_options.end(), bl->m_options.cbegin(),
727 bl->m_options.cend());
728 m_blocks.erase(bl);
729 }
730 }
731}
732
733//==============================================================================
734//==============================================================================
735//==============================================================================
736inline std::string expandIncludes(std::string str) {
737 const std::string include_text = "#include";
738
739 for (auto ipos = str.find(include_text); ipos != std::string::npos;
740 ipos = str.find(include_text)) {
741 const auto start = std::min(str.find('"', ipos), str.find('<', ipos));
742 const auto end =
743 std::min(str.find('"', start + 1), str.find('>', start + 1));
744 const auto fname = str.substr(start + 1, end - start - 1);
745 str.erase(ipos, end - ipos + 1);
746 std::ifstream ifile(fname);
747 if (ifile.good()) {
748 str.insert(ipos, removeComments(file_to_string(ifile)));
749 }
750 }
751
752 return str;
753}
754
755//==============================================================================
756inline std::string removeSpaces(std::string str) {
757
758 bool inside = false;
759 auto lambda = [&inside](unsigned char x) {
760 if (x == '\"' || x == '\'')
761 inside = !inside;
762 return ((x == ' ' || x == '\t' || x == '\n') && !inside);
763 };
764
765 str.erase(std::remove_if(str.begin(), str.end(), lambda), str.end());
766
767 return str;
768}
769
770inline std::string removeQuoteMarks(std::string str) {
771
772 // remove ' and "
773 str.erase(std::remove_if(str.begin(), str.end(),
774 [](unsigned char x) { return x == '\''; }),
775 str.end());
776 str.erase(std::remove_if(str.begin(), str.end(),
777 [](unsigned char x) { return x == '\"'; }),
778 str.end());
779
780 return str;
781}
782
783//==============================================================================
784inline void removeBlockComments(std::string &input) {
785 for (auto posi = input.find("/*"); posi != std::string::npos;
786 posi = input.find("/*")) {
787 auto posf = input.find("*/");
788 if (posf != std::string::npos) {
789 input = input.substr(0, posi) + input.substr(posf + 2);
790 } else {
791 input = input.substr(0, posi);
792 }
793 }
794}
795
796//==============================================================================
797inline std::string removeComments(const std::string &input) {
798 std::string str = "";
799 {
800 std::string line;
801 std::stringstream stream1(input);
802 while (std::getline(stream1, line, '\n')) {
803 // const auto comm1 = line.find('!'); // nb: char, NOT string literal!
804 // auto comm2 = line.find('#');
805 const auto comm3 = line.find("//"); // str literal here
806 // const auto comm = std::min({comm1, comm3});
807 str += line.substr(0, comm3);
808 str += '\n';
809 }
810 }
811 removeBlockComments(str);
812
813 return str;
814}
815
816//==============================================================================
817template <typename T>
818T inline parse_str_to_T(const std::string &value_as_str) {
819 if constexpr (std::is_same_v<T, std::string>) {
820 // already a string, just return value
821 return value_as_str;
822 } else {
823 // T is not a string: convert using stringstream
824 T value_T;
825 std::stringstream ss(value_as_str);
826 ss >> value_T;
827 return value_T;
828 }
829}
830
831//==============================================================================
832inline std::string file_to_string(const std::istream &file) {
833 std::string out;
834 if (!file)
835 return "";
836 // Horribly inneficient...
837 std::ostringstream ss;
838 ss << file.rdbuf();
839 return ss.str();
840}
841
842} // namespace IO
Holds list of Options, and a list of other InputBlocks. Can be initialised with a list of options,...
Definition InputBlock.hpp:153
InputBlock(std::string_view name, const std::istream &file)
Construct from plain text file, in Block{option=value;} format.
Definition InputBlock.hpp:174
bool check(const std::vector< std::pair< std::string, std::string > > &list, bool print=false) const
Override for when condidering current block.
Definition InputBlock.hpp:278
bool has_block(std::string_view name) const
Checks if block 'name' is present in current block.
Definition InputBlock.hpp:247
std::optional< Option > getOption(std::string_view key) const
Get an 'Option' (kay, value) - rarely needed.
Definition InputBlock.hpp:476
InputBlock(std::string_view name, const std::string &string_input)
Construct from a string with the correct Block{option=value;} format.
Definition InputBlock.hpp:168
void add(InputBlock block, bool merge=false)
Add a new InputBlock (merge: will be merged with existing if names match)
Definition InputBlock.hpp:307
bool check(std::initializer_list< std::string > blocks, const std::vector< std::pair< std::string, std::string > > &list, bool print=false) const
Check all the options and blocks in this; if any of them are not present in 'list',...
Definition InputBlock.hpp:606
void print(std::ostream &os=std::cout, int indent_depth=0) const
Prints options to screen in user-friendly form. Same form as input string. By default prints to cout,...
Definition InputBlock.hpp:486
const std::vector< Option > & options() const
Return const reference to list of options.
Definition InputBlock.hpp:191
InputBlock(std::string_view name, std::initializer_list< Option > options={})
Construct from literal list of 'Options' (see Option struct)
Definition InputBlock.hpp:164
InputBlock get_block(std::string_view name) const
If block is present, returns a copy of it. If not, returns empty block.
Definition InputBlock.hpp:239
const std::vector< InputBlock > & blocks() const
Return const reference to list of blocks.
Definition InputBlock.hpp:193
std::optional< InputBlock > getBlock(std::string_view name) const
Returns optional InputBlock. Contains InputBlock if block of given name exists; empty otherwise.
Definition InputBlock.hpp:454
bool option_is_set(std::string_view key) const
Check if option is present AND has been set.
Definition InputBlock.hpp:218
bool has_option(std::string_view key) const
Check is option is present (even if not set) in current block.
Definition InputBlock.hpp:212
T get(std::string_view key, T default_value) const
If 'key' exists in the options, returns value. Else, returns default_value. Note: If two keys with sa...
Definition InputBlock.hpp:428
friend bool operator==(InputBlock block, std::string_view name)
Comparison of blocks compares the 'name'.
Definition InputBlock.hpp:333
bool has_block(std::initializer_list< std::string > blocks, std::string_view name) const
Checks if block 'name' is present in nesteded block.
Definition InputBlock.hpp:252
InputBlock()
Default constructor: name will be blank.
Definition InputBlock.hpp:161
In-out (timers, profilers, and read/write data)
Definition ChronoTimer.hpp:9
void unkown_option(std::string_view test_string, const std::vector< std::string > &list)
Prints unkown option warning, with suggested alternative.
Definition InputBlock.hpp:19
void removeBlockComments(std::string &input)
Removes all c++ style block comments from a string.
Definition InputBlock.hpp:784
std::string expandIncludes(std::string input)
Expands "#include" files.
Definition InputBlock.hpp:736
std::string removeSpaces(std::string str)
Removes all white space (space, tab, newline), except for those in quotes.
Definition InputBlock.hpp:756
void print_line(const char c=' *', const int num=80)
Prints a line of 'c' characters (dflt '*'), num chars long (dflt 80) to cout.
Definition InputBlock.hpp:85
std::string file_to_string(const std::istream &file)
Parses entire file into string. Note: v. inefficient.
Definition InputBlock.hpp:832
std::string removeComments(const std::string &input)
Removes all c++ style comments from a string (block and line)
Definition InputBlock.hpp:797
T parse_str_to_T(const std::string &value_as_str)
Parses a string to type T by stringstream.
Definition InputBlock.hpp:818
std::string removeQuoteMarks(std::string str)
Removes all quote marks.
Definition InputBlock.hpp:770
std::string ci_closest_match(const std::string_view test_string, const std::vector< std::string > &list)
Finds the closest match (case insensitive) in list to test_string (return iterator)
Definition String.hpp:231
auto ci_Levenstein(std::string_view a, std::string_view b)
A simple non-optimised implementation of the Levenshtein distance (case insensitive)
Definition String.hpp:194
bool ci_wc_compare(std::string_view s1, std::string_view s2)
Compares two strings, s1 and s2. s2 may contain ONE wildcard ('*') which will match anything....
Definition String.hpp:140
Class to determine if a class template in vector.
Definition InputBlock.hpp:53
Simple struct; holds key-value pair, both strings. == compares key.
Definition InputBlock.hpp:116