diff options
author | John Ericson <John.Ericson@Obsidian.Systems> | 2022-02-28 18:04:39 +0000 |
---|---|---|
committer | John Ericson <John.Ericson@Obsidian.Systems> | 2022-02-28 18:29:33 +0000 |
commit | c863e5f338947ecff275a67725ecf50b2a47bdb5 (patch) | |
tree | 733893d760809edcbc55c7aa8078ab84fcd2aa73 /src/toml11/toml/combinator.hpp | |
parent | 7869be49c2735280ceabbd13c087b4a06444ae63 (diff) | |
parent | b592359c565e0220545ba146b32f367e4ecdb23f (diff) |
Merge remote-tracking branch 'upstream/master' into trustless-remote-builder-simple
Diffstat (limited to 'src/toml11/toml/combinator.hpp')
-rw-r--r-- | src/toml11/toml/combinator.hpp | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/src/toml11/toml/combinator.hpp b/src/toml11/toml/combinator.hpp new file mode 100644 index 000000000..33ecca1eb --- /dev/null +++ b/src/toml11/toml/combinator.hpp @@ -0,0 +1,306 @@ +// Copyright Toru Niina 2017. +// Distributed under the MIT License. +#ifndef TOML11_COMBINATOR_HPP +#define TOML11_COMBINATOR_HPP +#include <cassert> +#include <cctype> +#include <cstdio> + +#include <array> +#include <iomanip> +#include <iterator> +#include <limits> +#include <type_traits> + +#include "region.hpp" +#include "result.hpp" +#include "traits.hpp" +#include "utility.hpp" + +// they scans characters and returns region if it matches to the condition. +// when they fail, it does not change the location. +// in lexer.hpp, these are used. + +namespace toml +{ +namespace detail +{ + +// to output character as an error message. +inline std::string show_char(const char c) +{ + // It suppresses an error that occurs only in Debug mode of MSVC++ on Windows. + // I'm not completely sure but they check the value of char to be in the + // range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes + // has negative value (if char has sign). So here it re-interprets c as + // unsigned char through pointer. In general, converting pointer to a + // pointer that has different type cause UB, but `(signed|unsigned)?char` + // are one of the exceptions. Converting pointer only to char and std::byte + // (c++17) are valid. + if(std::isgraph(*reinterpret_cast<unsigned char const*>(std::addressof(c)))) + { + return std::string(1, c); + } + else + { + std::array<char, 5> buf; + buf.fill('\0'); + const auto r = std::snprintf( + buf.data(), buf.size(), "0x%02x", static_cast<int>(c) & 0xFF); + (void) r; // Unused variable warning + assert(r == static_cast<int>(buf.size()) - 1); + return std::string(buf.data()); + } +} + +template<char C> +struct character +{ + static constexpr char target = C; + + static result<region, none_t> + invoke(location& loc) + { + if(loc.iter() == loc.end()) {return none();} + const auto first = loc.iter(); + + const char c = *(loc.iter()); + if(c != target) + { + return none(); + } + loc.advance(); // update location + + return ok(region(loc, first, loc.iter())); + } +}; +template<char C> +constexpr char character<C>::target; + +// closed interval [Low, Up]. both Low and Up are included. +template<char Low, char Up> +struct in_range +{ + // assuming ascii part of UTF-8... + static_assert(Low <= Up, "lower bound should be less than upper bound."); + + static constexpr char upper = Up; + static constexpr char lower = Low; + + static result<region, none_t> + invoke(location& loc) + { + if(loc.iter() == loc.end()) {return none();} + const auto first = loc.iter(); + + const char c = *(loc.iter()); + if(c < lower || upper < c) + { + return none(); + } + + loc.advance(); + return ok(region(loc, first, loc.iter())); + } +}; +template<char L, char U> constexpr char in_range<L, U>::upper; +template<char L, char U> constexpr char in_range<L, U>::lower; + +// keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char. +// for detecting invalid characters, like control sequences in toml string. +template<typename Combinator> +struct exclude +{ + static result<region, none_t> + invoke(location& loc) + { + if(loc.iter() == loc.end()) {return none();} + auto first = loc.iter(); + + auto rslt = Combinator::invoke(loc); + if(rslt.is_ok()) + { + loc.reset(first); + return none(); + } + loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but... + return ok(region(loc, first, loc.iter())); + } +}; + +// increment `iter`, if matches. otherwise, just return empty string. +template<typename Combinator> +struct maybe +{ + static result<region, none_t> + invoke(location& loc) + { + const auto rslt = Combinator::invoke(loc); + if(rslt.is_ok()) + { + return rslt; + } + return ok(region(loc)); + } +}; + +template<typename ... Ts> +struct sequence; + +template<typename Head, typename ... Tail> +struct sequence<Head, Tail...> +{ + static result<region, none_t> + invoke(location& loc) + { + const auto first = loc.iter(); + auto rslt = Head::invoke(loc); + if(rslt.is_err()) + { + loc.reset(first); + return none(); + } + return sequence<Tail...>::invoke(loc, std::move(rslt.unwrap()), first); + } + + // called from the above function only, recursively. + template<typename Iterator> + static result<region, none_t> + invoke(location& loc, region reg, Iterator first) + { + const auto rslt = Head::invoke(loc); + if(rslt.is_err()) + { + loc.reset(first); + return none(); + } + reg += rslt.unwrap(); // concat regions + return sequence<Tail...>::invoke(loc, std::move(reg), first); + } +}; + +template<typename Head> +struct sequence<Head> +{ + // would be called from sequence<T ...>::invoke only. + template<typename Iterator> + static result<region, none_t> + invoke(location& loc, region reg, Iterator first) + { + const auto rslt = Head::invoke(loc); + if(rslt.is_err()) + { + loc.reset(first); + return none(); + } + reg += rslt.unwrap(); // concat regions + return ok(reg); + } +}; + +template<typename ... Ts> +struct either; + +template<typename Head, typename ... Tail> +struct either<Head, Tail...> +{ + static result<region, none_t> + invoke(location& loc) + { + const auto rslt = Head::invoke(loc); + if(rslt.is_ok()) {return rslt;} + return either<Tail...>::invoke(loc); + } +}; +template<typename Head> +struct either<Head> +{ + static result<region, none_t> + invoke(location& loc) + { + return Head::invoke(loc); + } +}; + +template<typename T, typename N> +struct repeat; + +template<std::size_t N> struct exactly{}; +template<std::size_t N> struct at_least{}; +struct unlimited{}; + +template<typename T, std::size_t N> +struct repeat<T, exactly<N>> +{ + static result<region, none_t> + invoke(location& loc) + { + region retval(loc); + const auto first = loc.iter(); + for(std::size_t i=0; i<N; ++i) + { + auto rslt = T::invoke(loc); + if(rslt.is_err()) + { + loc.reset(first); + return none(); + } + retval += rslt.unwrap(); + } + return ok(std::move(retval)); + } +}; + +template<typename T, std::size_t N> +struct repeat<T, at_least<N>> +{ + static result<region, none_t> + invoke(location& loc) + { + region retval(loc); + + const auto first = loc.iter(); + for(std::size_t i=0; i<N; ++i) + { + auto rslt = T::invoke(loc); + if(rslt.is_err()) + { + loc.reset(first); + return none(); + } + retval += rslt.unwrap(); + } + while(true) + { + auto rslt = T::invoke(loc); + if(rslt.is_err()) + { + return ok(std::move(retval)); + } + retval += rslt.unwrap(); + } + } +}; + +template<typename T> +struct repeat<T, unlimited> +{ + static result<region, none_t> + invoke(location& loc) + { + region retval(loc); + while(true) + { + auto rslt = T::invoke(loc); + if(rslt.is_err()) + { + return ok(std::move(retval)); + } + retval += rslt.unwrap(); + } + } +}; + +} // detail +} // toml +#endif// TOML11_COMBINATOR_HPP |