9 #include "boost/phoenix/bind.hpp" 10 #include "boost/phoenix/operator.hpp" 11 #include "boost/spirit/include/qi.hpp" 12 #include "boost/spirit/include/qi_no_skip.hpp" 13 #include "boost/spirit/include/support_istream_iterator.hpp" 14 #include "boost/spirit/repository/home/qi/primitive/iter_pos.hpp" 16 #include "cetlib/canonical_number.h" 17 #include "cetlib/canonical_string.h" 18 #include "cetlib/include.h" 19 #include "cetlib/includer.h" 32 namespace ascii = ::boost::spirit::ascii;
33 namespace phx = ::boost::phoenix;
34 namespace qi = ::boost::spirit::qi;
43 using boost::spirit::repository::qi::iter_pos;
47 using namespace shims;
52 using namespace fhicl;
68 check_element_protections(std::string
const& name,
77 <<
"Nested item " << name <<
" has protection " 82 <<
", which is incompatible with an enclosing item's protection of " 87 std::size_t count = 0;
89 check_element_protections(
94 std::string sname(name);
108 check_element_protections(name, v.
protection, v);
113 set_protection(std::string
const& name,
117 if (m == binding_modifier::NONE) {
122 check_protection(name, v);
128 static std::string
const canon_nil(9,
'\0');
133 canon_inf(std::string
const& inf)
135 return inf[0] ==
'i' ? (
'+' + inf) : inf;
139 canon_num(std::string
const& num)
142 return cet::canonical_number(num, result) ?
145 <<
"The string '" << num
146 <<
"' is not representable as a canonical number.";
150 canon_str(std::string
const& str)
153 return cet::canonical_string(str, result) ?
156 <<
"The string " + str +
157 " is not representable as a canonical string.\n" 158 <<
"It is likely you have an unescaped (or incorrectly escaped) " 163 xvalue_vp(
bool const b,
value_tag const t, std::any
const v)
173 return std::make_pair(c1, c2);
177 map_insert(std::string
const& name,
182 set_protection(name, m, value);
183 auto const i = t.
find(name);
185 auto existing_protection = i->second.protection;
188 <<
"Inserting name " << name <<
" would increase protection from " 189 <<
to_string(existing_protection) <<
" to " 191 << i->second.pretty_src_info() <<
")\n";
193 switch (i->second.protection) {
194 case Protection::NONE:
196 case Protection::PROTECT_IGNORE:
199 case Protection::PROTECT_ERROR:
201 <<
'"' << name <<
"\" is protected on " << i->second.pretty_src_info()
209 map_insert_loc(std::string
const& name,
214 cet::includer
const& s)
216 map_insert(name, m, value, t);
220 <<
" at " << s.highlighted_whereis(pos) <<
'\n';
227 v.emplace_back(std::move(xval));
231 map_erase(std::string
const& name,
table_t& t)
233 auto const i = t.
find(name);
237 switch (i->second.protection) {
238 case Protection::NONE:
240 case Protection::PROTECT_IGNORE:
242 case Protection::PROTECT_ERROR:
244 <<
"Unable to erase " << name <<
" due to protection.\n";
249 map_erase_loc(std::string
const& name,
252 cet::includer
const& s)
259 <<
" at " << s.highlighted_whereis(pos) <<
'\n';
266 template <
typename Skip>
267 struct value_parser : qi::grammar<FwdIter, extended_value(), Skip> {
268 using atom_token = qi::rule<FwdIter, atom_t(), Skip>;
269 using complex_token = qi::rule<FwdIter, complex_t(), Skip>;
270 using sequence_token = qi::rule<FwdIter, sequence_t(), Skip>;
271 using table_token = qi::rule<FwdIter, table_t(), Skip>;
272 using value_token = qi::rule<FwdIter, extended_value(), Skip>;
281 atom_token nil, boolean;
283 atom_token squoted, dquoted;
284 atom_token number, string, name, catchall;
286 complex_token complex;
287 sequence_token sequence;
295 using Skip = qi::rule<iter_t>;
296 struct document_parser : qi::grammar<FwdIter, void(), Skip> {
297 using val_parser = value_parser<Skip>;
298 using atom_token = val_parser::atom_token;
299 using sequence_token = val_parser::sequence_token;
300 using table_token = val_parser::table_token;
301 using value_token = val_parser::value_token;
302 using nothing_token = qi::rule<FwdIter, void(), Skip>;
304 explicit document_parser(cet::includer
const& s);
307 bool in_prolog{
false};
310 cet::includer
const& sref;
313 atom_token name, qualname, noskip_qualname, localref, dbref;
314 sequence_token sequence;
317 nothing_token prolog, document;
321 local_lookup(std::string
const& name, iter_t
const pos)
331 <<
"at " << sref.highlighted_whereis(pos) <<
"\n";
335 database_lookup(iter_t
const pos)
338 "Database lookup error")
339 <<
"at " << sref.highlighted_whereis(pos)
340 <<
"\nFHiCL-cpp database lookup not yet available.\n";
344 insert_table_in_table(std::string
const& name,
table_t& t, iter_t
const pos)
349 <<
"key \"" << name <<
"\" does not refer to a table at " 350 << sref.highlighted_whereis(pos) <<
"\n";
352 auto const& incoming = std::any_cast<
table_t const&>(xval.
value);
353 for (
auto const& [name, value] : incoming) {
354 auto& element = t[name];
357 auto const incoming_protection = value.protection;
358 if (incoming_protection > element.protection) {
360 <<
"@table::" << name <<
": inserting name " << name
361 <<
" would increase protection from " 362 <<
to_string(element.protection) <<
" to " 363 <<
to_string(incoming_protection) <<
"\n(previous definition on " 364 << element.pretty_src_info() <<
")\n";
366 switch (element.protection) {
367 case Protection::NONE:
369 case Protection::PROTECT_IGNORE:
371 case Protection::PROTECT_ERROR:
373 <<
"@table::" << name <<
": inserting name " << name
374 <<
"would violate protection on existing item" 375 <<
"\n(previous definition on " << element.pretty_src_info()
380 element.set_prolog(in_prolog);
381 element.set_src_info(sref.src_whereis(pos));
386 insert_table(std::string
const& name, iter_t
const pos)
391 <<
"key \"" << name <<
"\" does not refer to a table at " 392 << sref.highlighted_whereis(pos) <<
"\n";
394 auto const& incoming = std::any_cast<
table_t const&>(xval.
value);
395 for (
auto const& [name, value] : incoming) {
396 auto element =
value;
397 element.set_prolog(in_prolog);
398 element.set_src_info(sref.src_whereis(pos));
399 tbl.insert(name, std::move(element));
404 seq_insert_sequence(std::string
const& name,
411 <<
"key \"" << name <<
"\" does not refer to a sequence at " 412 << sref.highlighted_whereis(pos) <<
"\n";
415 auto it = v.insert(v.end(), incoming.cbegin(), incoming.cend());
416 for (
auto const e = v.end(); it !=
e; ++it) {
418 it->protection = Protection::NONE;
419 it->set_prolog(in_prolog);
420 it->set_src_info(sref.src_whereis(pos));
425 xvalue_(
value_tag const t, std::any
const v, iter_t
const pos)
433 return phx::bind(&document_parser::xvalue_,
this, t, qi::_2, qi::_1);
437 tbl_erase(std::string
const& name, iter_t
const pos)
439 tbl.erase(name, in_prolog);
444 <<
" at " << sref.highlighted_whereis(pos) <<
'\n';
448 tbl_insert(std::string
const& name,
453 set_protection(name, m, value);
454 tbl.insert(name, value);
459 <<
" at " << sref.highlighted_whereis(pos) <<
'\n';
463 set_in_prolog(
bool const value)
472 template <
class Skip>
473 value_parser<Skip>::value_parser() : value_parser::base_type{value}
475 nil = lexeme[(qi::string(
"@nil") >>
476 !(graph - char_(
",]}")))[_val = phx::bind(
canon_nil)]];
477 boolean = lexeme[(qi::string(
"true") | qi::string(
"false")) >>
478 !(graph - char_(
",]}"))];
479 inf = lexeme[-(qi::string(
"+") | qi::string(
"-")) >>
480 qi::string(
"infinity") >> !(graph - char_(
"),]}"))];
481 squoted = lexeme[char_(
'\'') >> *(char_ - char_(
'\'')) >> char_(
'\'') >>
482 !(graph - char_(
",]}"))];
484 lexeme[
raw[char_(
'\"') >> *(qi::string(
"\\\"") | (char_ - char_(
'\"'))) >>
485 char_(
'\"') >> !(graph - char_(
",]}"))]];
487 (fhicl::uint[_val = phx::bind(canon_num, qi::_1)] |
488 inf[_val = phx::bind(canon_inf, qi::_1)] | fhicl::real[_val = qi::_1] |
489 fhicl::hex[_val = qi::_1] |
fhicl::bin[_val = qi::_1]);
490 string = (fhicl::ass | fhicl::dss | squoted |
491 dquoted)[_val = phx::bind(canon_str, ref(qi::_1))];
492 name = fhicl::ass[_val = qi::_1];
493 complex = (
lit(
'(') > number >
lit(
',') > number >
494 lit(
')'))[_val = phx::bind(cplx, qi::_1, qi::_2)];
495 sequence =
lit(
'[') > -(value %
',') >
lit(
']');
498 *((name >> fhicl::binding >>
499 value)[phx::bind(map_insert, ref(qi::_1), qi::_2, ref(qi::_3), _val)] |
501 (
lit(
':') >
lit(
"@erase")))[phx::bind(map_erase, ref(qi::_1), _val)]) >
503 id =
lit(
"@id::") > no_skip[fhicl::dbid][_val = qi::_1];
504 catchall = shims::catchall[_val = phx::bind(canon_str, ref(qi::_1))];
505 value = (nil[_val = phx::bind(xvalue_vp,
false,
NIL, qi::_1)] |
506 boolean[_val = phx::bind(xvalue_vp,
false,
BOOL, qi::_1)] |
507 number[_val = phx::bind(xvalue_vp,
false,
NUMBER, qi::_1)] |
508 complex[_val = phx::bind(xvalue_vp,
false,
COMPLEX, qi::_1)] |
509 string[_val = phx::bind(xvalue_vp,
false,
STRING, qi::_1)] |
510 sequence[_val = phx::bind(xvalue_vp,
false,
SEQUENCE, qi::_1)] |
511 table[_val = phx::bind(xvalue_vp,
false,
TABLE, qi::_1)] |
512 id[_val = phx::bind(xvalue_vp,
false,
TABLEID, qi::_1)] |
513 catchall[_val = phx::bind(xvalue_vp,
false,
STRING, qi::_1)]);
514 nil.name(
"nil token");
515 boolean.name(
"boolean token");
516 inf.name(
"inf token");
517 squoted.name(
"squoted token");
518 dquoted.name(
"dquoted token");
519 number.name(
"number atom");
520 string.name(
"string atom");
521 name.name(
"name atom");
522 complex.name(
"complex atom");
523 sequence.name(
"sequence");
527 catchall.name(
"catchall atom");
532 document_parser::document_parser(cet::includer
const& s)
533 : document_parser::base_type{document}, sref{s}
537 fhicl::ass[_val = qi::_1] >>
538 *((char_(
'.') > fhicl::ass)[_val += qi::_1 + qi::_2] |
539 (char_(
'[') > fhicl::uint >
540 char_(
']'))[_val += qi::_1 + qi::_2 + qi::_3]);
545 no_skip[fhicl::ass][_val = qi::_1] >>
546 *((char_(
'.') > fhicl::ass)[_val += qi::_1 + qi::_2] |
547 (char_(
'[') > fhicl::uint >
548 char_(
']'))[_val += qi::_1 + qi::_2 + qi::_3]);
552 localref =
lit(
"@local::") > noskip_qualname;
553 dbref =
lit(
"@db::") > noskip_qualname;
558 -(((value[phx::bind(seq_insert_value, ref(qi::_1), _val)]) |
559 ((iter_pos >>
lit(
"@sequence::")) >
560 noskip_qualname)[phx::bind(&document_parser::seq_insert_sequence,
566 ((value[phx::bind(seq_insert_value, ref(qi::_1), _val)]) |
567 ((iter_pos >>
lit(
"@sequence::")) >
568 noskip_qualname)[phx::bind(&document_parser::seq_insert_sequence,
572 qi::_1)])) >
lit(
']');
575 *((iter_pos >> name >> fhicl::binding >>
value)[phx::bind(&map_insert_loc,
582 (iter_pos >> name >> (
lit(
':') >
lit(
"@erase")))[phx::bind(
583 &map_erase_loc, ref(qi::_2), _val, qi::_1, sref)] |
584 ((iter_pos >>
lit(
"@table::")) >
585 noskip_qualname)[phx::bind(&document_parser::insert_table_in_table,
589 qi::_1)]) >
lit(
'}');
593 #pragma clang diagnostic push 594 #pragma clang diagnostic ignored "-Wunsequenced" 597 ((iter_pos >> vp.nil)[_val = xvalue_for(
NIL)] |
598 (iter_pos >> vp.boolean)[_val = xvalue_for(
BOOL)] |
599 (iter_pos >> vp.number)[_val = xvalue_for(
NUMBER)] |
600 (iter_pos >> vp.complex)[_val = xvalue_for(
COMPLEX)] |
601 (iter_pos >> vp.string)[_val = xvalue_for(
STRING)] |
603 localref)[_val = phx::bind(
604 &document_parser::local_lookup,
this, qi::_2, qi::_1)] |
605 (iter_pos >> dbref)[_val = phx::bind(
606 &document_parser::database_lookup,
this, qi::_1)] |
607 (iter_pos >> vp.id)[_val = xvalue_for(
TABLEID)] |
608 (iter_pos >> sequence)[_val = xvalue_for(
SEQUENCE)] |
609 (iter_pos >> table)[_val = xvalue_for(
TABLE)] |
610 (iter_pos >> vp.catchall)[_val = xvalue_for(
STRING)]);
612 #pragma clang diagnostic pop 615 lit(
"BEGIN_PROLOG")[phx::bind(
616 &document_parser::set_in_prolog,
this,
true)] >
617 *((iter_pos >> qualname >> fhicl::binding >>
618 value)[phx::bind(&document_parser::tbl_insert,
624 (iter_pos >> qualname >> (
lit(
':') >
lit(
"@erase")))[phx::bind(
625 &document_parser::tbl_erase,
this, qi::_2, qi::_1)] |
626 ((iter_pos >>
lit(
"@table::")) > noskip_qualname)[phx::bind(
627 &document_parser::insert_table,
this, qi::_2, qi::_1)]) >
629 "END_PROLOG")[phx::bind(&document_parser::set_in_prolog,
this,
false)];
630 document = (*prolog) >>
631 *((iter_pos >> qualname >> fhicl::binding >>
632 value)[phx::bind(&document_parser::tbl_insert,
638 (iter_pos >> qualname >> (
lit(
':') >
lit(
"@erase")))[phx::bind(
639 &document_parser::tbl_erase,
this, qi::_2, qi::_1)] |
640 ((iter_pos >>
lit(
"@table::")) > noskip_qualname)[phx::bind(
641 &document_parser::insert_table,
this, qi::_2, qi::_1)]);
642 name.name(
"name atom");
643 localref.name(
"localref atom");
644 dbref.name(
"dbref atom");
645 qualname.name(
"qualified name");
646 noskip_qualname.name(
"qualified name (no pre-skip)");
647 sequence.name(
"sequence");
650 prolog.name(
"prolog");
651 document.name(
"document");
660 std::string& unparsed)
662 using ws_t = qi::rule<FwdIter>;
663 ws_t whitespace = space |
lit(
'#') >> *(char_ - eol) >> eol |
664 lit(
"//") >> *(char_ - eol) >> eol;
665 value_parser<ws_t> p;
666 auto begin = s.begin();
667 auto const end = s.end();
669 qi::phrase_parse(
begin,
end, p >> *whitespace, whitespace, result) &&
679 parse_document_(cet::includer s)
681 qi::rule<iter_t> whitespace = space |
lit(
'#') >> *(char_ - eol) >> eol |
682 lit(
"//") >> *(char_ - eol) >> eol;
683 document_parser p(s);
684 auto begin = s.begin();
685 auto const end = s.end();
688 b = qi::phrase_parse(
begin,
end, p, whitespace);
690 catch (qi::expectation_failure<iter_t>
const&
e) {
693 std::string
const unparsed(
begin,
end);
694 if (b && unparsed.empty()) {
695 return std::move(p.tbl);
699 << s.highlighted_whereis(
begin) <<
"\n";
701 if (unparsed.find(
"BEGIN_PROLOG"s) == 0ull) {
702 e <<
"PROLOG blocks must be both contiguous and not nested.\n";
711 return parse_document_(cet::includer{filename, maker});
717 return parse_document_(cet::includer(is, maker));
723 std::istringstream is{s};
724 cet::filepath_maker m;
intermediate_table::complex_t complex_t
size_t erase(Key const &key)
bool parse_value_string(std::string const &s, extended_value &v, std::string &unparsed)
bool is_a(value_tag const t) const noexcept
decltype(auto) constexpr end(T &&obj)
ADL-aware version of std::end.
void set_src_info(std::string const &src)
decltype(auto) constexpr to_string(T &&obj)
ADL-aware version of std::to_string.
static std::string canon_nil()
extended_value::table_t table_t
fhicl::extended_value::sequence_t sequence_t
intermediate_table::atom_t atom_t
extended_value::atom_t atom_t
intermediate_table parse_document(std::string const &filename, cet::filepath_maker &maker)
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
std::string pretty_src_info() const
extended_value::complex_t complex_t
void set_prolog(bool new_prolog_state)
iterator find(Key const &key)
cet::coded_exception< error, detail::translate > exception
extended_value::sequence_t sequence_t