moved date and time stuff to separate header

also:
- merged all the separate integer parsing functions
- renamed the member functions of parse_error for clarity
- added the beginnings of a doxygen pipeline
This commit is contained in:
Mark Gillard 2020-01-11 23:15:24 +02:00
parent 537eb30080
commit ab5ffa5a3c
32 changed files with 2952 additions and 1821 deletions

View File

@ -3,9 +3,13 @@ version: 2.1
jobs:
linux_build:
docker:
- image: "debian:bullseye"
- image: debian:bullseye
steps:
- checkout
- run:
name: Pulling Catch2
command: |
git submodule update --init --recursive extern/Catch2
- run:
name: Installing python
command: |
@ -19,10 +23,6 @@ jobs:
command: |
apt-get -qq update && apt-get install -y git clang-9 g++-9 python3-pip ninja-build
pip3 install meson
- run:
name: Pulling submodules
command: |
git submodule update --init --recursive
- run:
name: Building with clang
command: |
@ -38,8 +38,70 @@ jobs:
command: |
cd build-clang && ninja test && cd ../build-gcc && ninja test
generate_dox:
docker:
- image: debian:bullseye
steps:
- checkout
- run:
name: Installing dependencies
command: |
git submodule update --init extern/m.css
apt-get -qq update && apt-get install -y python3 doxygen python3-pip cmake clang-9 flex bison
pip3 install beautifulsoup4 jinja2 pygments html5lib
- run:
name: Building and installing Doxygen 1.8.17
command: |
wget http://doxygen.nl/files/doxygen-1.8.17.src.tar.gz
tar xvzf doxygen-1.8.17.src.tar.gz
rm doxygen-1.8.17.src.tar.gz
cd doxygen-1.8.17
CC=clang-9 CXX=clang++-9 cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -Wno-dev -Wno-deprecated
make install
- run:
name: Generating documentation
command: |
cd python && python3 generate_documentation.py
- persist_to_workspace:
root: docs
paths: html
deploy_dox:
docker:
- image: node:8.10.0
steps:
- checkout
- attach_workspace:
at: docs
- run:
name: Disable jekyll builds
command: |
touch docs/html/.nojekyll
- run:
name: Installing dependencies
command: |
npm install -g --silent gh-pages
git config user.email "ci-build@tomlplusplus.com"
git config user.name "ci-build"
- add_ssh_keys:
fingerprints:
- "a6:63:c0:a5:89:cf:2d:03:e7:c9:88:5d:c0:8c:39:e0"
- run:
name: Deploy docs to gh-pages branch
command: gh-pages --dotfiles --message "[skip ci] Updates" --dist docs/html
workflows:
version: 2
build:
jobs:
- linux_build
- generate_dox:
requires:
- linux_build
filters:
branches:
only: master
- deploy_dox:
requires:
- generate_dox

2
.gitignore vendored
View File

@ -7,6 +7,8 @@ meson-private/
build.ninja
compile_commands.json
[Bb]uild*/
[Dd]ocs/xml
[Dd]ocs/html
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "extern/Catch2"]
path = extern/Catch2
url = https://github.com/catchorg/Catch2.git
[submodule "extern/m.css"]
path = extern/m.css
url = https://github.com/mosra/m.css.git

384
docs/Doxyfile Normal file
View File

@ -0,0 +1,384 @@
# Doxyfile 1.8.16
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = toml++
PROJECT_NUMBER =
PROJECT_BRIEF =
PROJECT_LOGO =
OUTPUT_DIRECTORY = ./
CREATE_SUBDIRS = NO
ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
OUTPUT_TEXT_DIRECTION = None
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF = "The $name class" \
"The $name widget" \
"The $name file" \
is \
provides \
specifies \
contains \
represents \
a \
an \
the
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = YES
FULL_PATH_NAMES = YES
STRIP_FROM_PATH = ../
STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
JAVADOC_BANNER = NO
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 4
ALIASES = "cpp=@code{.cpp}" \
"ecpp=@endcode" \
"epp=@endcode" \
"detail=@details"
TCL_SUBST =
OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
OPTIMIZE_OUTPUT_SLICE = NO
EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 0
AUTOLINK_SUPPORT = YES
BUILTIN_STL_SUPPORT = YES
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = NO
DISTRIBUTE_GROUP_DOC = NO
GROUP_NESTED_COMPOUNDS = NO
SUBGROUPING = YES
INLINE_GROUPED_CLASSES = NO
INLINE_SIMPLE_STRUCTS = NO
TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 1
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = NO
EXTRACT_PRIVATE = NO
EXTRACT_PRIV_VIRTUAL = NO
EXTRACT_PACKAGE = NO
EXTRACT_STATIC = NO
EXTRACT_LOCAL_CLASSES = NO
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = YES
HIDE_UNDOC_CLASSES = YES
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = NO
HIDE_SCOPE_NAMES = NO
HIDE_COMPOUND_REFERENCE= NO
SHOW_INCLUDE_FILES = YES
SHOW_GROUPED_MEMB_INC = NO
FORCE_LOCAL_INCLUDES = NO
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
SORT_BRIEF_DOCS = YES
SORT_MEMBERS_CTORS_1ST = YES
SORT_GROUP_NAMES = NO
SORT_BY_SCOPE_NAME = NO
STRICT_PROTO_MATCHING = NO
GENERATE_TODOLIST = NO
GENERATE_TESTLIST = NO
GENERATE_BUGLIST = NO
GENERATE_DEPRECATEDLIST= NO
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
SHOW_FILES = YES
SHOW_NAMESPACES = YES
FILE_VERSION_FILTER =
LAYOUT_FILE =
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_AS_ERROR = YES
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE = ./doc/doxygen_warnings.log
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = ../include
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.c \
*.cc \
*.cxx \
*.cpp \
*.c++ \
*.java \
*.ii \
*.ixx \
*.ipp \
*.i++ \
*.inl \
*.idl \
*.ddl \
*.odl \
*.h \
*.hh \
*.hxx \
*.hpp \
*.h++ \
*.cs \
*.d \
*.php \
*.php4 \
*.php5 \
*.phtml \
*.inc \
*.m \
*.markdown \
*.md \
*.mm \
*.dox \
*.py \
*.pyw \
*.f90 \
*.f95 \
*.f03 \
*.f08 \
*.f \
*.for \
*.tcl \
*.vhd \
*.vhdl \
*.ucf \
*.qsf
RECURSIVE = YES
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS = *
EXAMPLE_RECURSIVE = NO
IMAGE_PATH = ./
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE =
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = NO
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = NO
REFERENCES_RELATION = NO
REFERENCES_LINK_SOURCE = NO
SOURCE_TOOLTIPS = YES
USE_HTAGS = NO
VERBATIM_HEADERS = NO
CLANG_ASSISTED_PARSING = NO
CLANG_OPTIONS =
CLANG_DATABASE_PATH =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = YES
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 224
HTML_COLORSTYLE_SAT = 50
HTML_COLORSTYLE_GAMMA = 67
HTML_TIMESTAMP = NO
HTML_DYNAMIC_MENUS = YES
HTML_DYNAMIC_SECTIONS = NO
HTML_INDEX_NUM_ENTRIES = 100
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = org.doxygen.Project
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
DOCSET_PUBLISHER_NAME = Publisher
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
CHM_INDEX_ENCODING =
BINARY_TOC = NO
TOC_EXPAND = NO
GENERATE_QHP = NO
QCH_FILE =
QHP_NAMESPACE = org.doxygen.Project
QHP_VIRTUAL_FOLDER = doc
QHP_CUST_FILTER_NAME =
QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
QHG_LOCATION =
GENERATE_ECLIPSEHELP = NO
ECLIPSE_DOC_ID = org.doxygen.Project
DISABLE_INDEX = NO
GENERATE_TREEVIEW = YES
ENUM_VALUES_PER_LINE = 4
TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
USE_MATHJAX = NO
MATHJAX_FORMAT = HTML-CSS
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
MATHJAX_EXTENSIONS =
MATHJAX_CODEFILE =
SEARCHENGINE = NO
SERVER_BASED_SEARCH = NO
EXTERNAL_SEARCH = NO
SEARCHENGINE_URL =
SEARCHDATA_FILE = searchdata.xml
EXTERNAL_SEARCH_ID =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
LATEX_MAKEINDEX_CMD = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4
EXTRA_PACKAGES =
LATEX_HEADER =
LATEX_FOOTER =
LATEX_EXTRA_STYLESHEET =
LATEX_EXTRA_FILES =
PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
LATEX_SOURCE_CODE = NO
LATEX_BIB_STYLE = plain
LATEX_TIMESTAMP = NO
LATEX_EMOJI_DIRECTORY =
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
RTF_SOURCE_CODE = NO
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_SUBDIR =
MAN_LINKS = NO
#---------------------------------------------------------------------------
# Configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_PROGRAMLISTING = YES
XML_NS_MEMB_FILE_SCOPE = NO
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED = __cplusplus=201703L
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = NO
#---------------------------------------------------------------------------
# Configuration options related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = NO
DIA_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = NO
DOT_NUM_THREADS = 0
DOT_FONTNAME = Helvetica
DOT_FONTSIZE = 10
DOT_FONTPATH =
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
UML_LIMIT_NUM_FIELDS = 10
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = YES
CALLER_GRAPH = YES
GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = png
INTERACTIVE_SVG = NO
DOT_PATH =
DOTFILE_DIRS =
MSCFILE_DIRS =
DIAFILE_DIRS =
PLANTUML_JAR_PATH =
PLANTUML_CFG_FILE =
PLANTUML_INCLUDE_PATH =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES

21
docs/Doxyfile-mcss Normal file
View File

@ -0,0 +1,21 @@
@INCLUDE = ./Doxyfile
GENERATE_HTML = NO
GENERATE_XML = YES
XML_PROGRAMLISTING = NO
HTML_EXTRA_STYLESHEET = https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600 \
../css/m-dark+documentation.compiled.css \
tomlplusplus.css
WARN_AS_ERROR = NO
TAB_SIZE = 4
HTML_EXTRA_FILES = tomlplusplus.js
##! M_THEME_COLOR = #2d2d30
##! M_LINKS_NAVBAR1 = namespaces
##! M_LINKS_NAVBAR2 = annotated
##! M_SEARCH_DOWNLOAD_BINARY = NO
##! M_CLASS_TREE_EXPAND_LEVELS = 1
##! M_FILE_TREE_EXPAND_LEVELS = 3
##! M_PAGE_FINE_PRINT = \
##! <a target="_blank" href="https://github.com/marzer/tomlplusplus/">Github</a> &bull; \
##! <a target="_blank" href="https://github.com/marzer/tomlplusplus/issues">Report an issue</a> \
##! <br><br><div id="tpp-custom-footer"></div>
##! M_HTML_HEADER = <script src="tomlplusplus.js"></script>

218
docs/tomlplusplus.css Normal file
View File

@ -0,0 +1,218 @@
table.m-table th
{
color: #ffe698;
}
article section > h2
{
padding-top: 0.5rem;
padding-bottom: 0.1rem;
background-color: #282e36;
padding-left: 1rem;
margin-left: -1rem;
margin-right: -1rem;
margin-top: 2rem;
border-color: #282e36;
border-style: solid;
border-width: 0.0625rem;
border-left-width: 0.25rem;
border-radius: 0.2rem;
}
article section:target
{
border-radius: 0.2rem;
}
article section:target > h2
{
border-color: #a5c9ea;
}
table.m-table thead th
{
border-bottom-width: 0.0rem;
}
table.m-table tbody td
{
border: 0px;
}
table.m-table thead tr:first-child th
{
border-top-width: 0.1rem;
}
code.m-code, aside code
{
margin-left: 0.1em;
padding-left: 0.2em;
padding-right: 0.1em;
}
dl.m-doc dd
{
margin-bottom: 0.8rem;
}
a.tpp-external
{
font-weight: normal;
}
.m-doc-template a, dl.m-doc dd a, ul.m-doc li > span.m-doc a
{
color: #858585;
}
.m-doc-template a.tpp-external:hover,
dl.m-doc dd a.tpp-external:hover,
ul.m-doc li > span.m-doc a.tpp-external:hover
{
color: inherit;
}
.tpp-swatch, .tpp-enable-if > *
{
display: inline-block;
border-radius: 0.2rem;
}
.tpp-enable-if
{
margin-bottom: 2px;
}
.tpp-enable-if > *
{
background-clip: padding-box !important;
padding: 0px 2px;
text-decoration: none;
}
.tpp-enable-if > a
{
white-space: nowrap;
font-size: 0.8rem;
font-weight: bold;
background-color: #858585;
color: #050505;
padding-bottom: 0px;
margin: 0px 1px;
margin-bottom: 2px;
}
.tpp-enable-if > a:hover
{
background-color: #747474;
color: initial;
}
.tpp-enable-if > span
{
display: none;
padding-left: 2em;
}
.tpp-swatch
{
min-width: 3em;
min-height: 1em;
}
body > header > nav
{
background-color: #1e1e1e;
}
html
{
background-color: #2d2d30;
}
pre, code, .m-label/*.m-flat.m-success*/, .tpp-enable-if > a
{
font-family: 'Consolas', monospace;
}
pre, .m-doc-search-content /*, .tpp-enable-if > full*/
{
background-color: #1e1e1e;
}
pre.m-code, code
{
background-color: #1e1e1e88;
}
.m-code .c1 /* comments */
{
color: rgb(87,166,74);
}
.m-code .mi, .m-code .mf /* literals */
{
color: rgb(181,206,168);
}
.m-code .k /* keywords */
{
color: rgb(86,156,214);
}
.m-code .n /* names */
{
color: rgb(220,220,220);
}
.m-code .p /* punctuation */
{
color: rgb(120,120,120);
}
article section.m-doc-details > div > h3:first-child, article section > h2, body > footer > nav
{
background-color: #252526;
}
.tpp-external-navbar
{
background-color: #007acc11;
}
.m-label:not(.m-flat)
{
font-weight: bold;
}
header
{
position: fixed;
left: 0;
right: 0;
top: 0;
z-index: 100;
}
body
{
margin-top: 3rem;
}
div.m-doc-include
{
font-size: 0.9rem;
}
.m-doc-include span.cp
{
display: none;
}
.m-doc-include a.cpf
{
color: #9999AA;
}
article, article > header, article section
{
margin-bottom: 3em;
background-color: red;
background-color: inherit;
}

18
docs/tomlplusplus.js Normal file
View File

@ -0,0 +1,18 @@
function ToggleEnableIf(anchor)
{
if (!anchor.hasOwnProperty('enableIfHidden'))
anchor['enableIfHidden'] = true;
anchor.enableIfHidden = !anchor.enableIfHidden;
content = anchor.parentNode.getElementsByTagName('span')[0]
if (anchor.enableIfHidden)
{
anchor.style.display = "";
content.style.display = "";
}
else
{
anchor.style.display = "none";
content.style.display = "block";
}
}

View File

@ -31,13 +31,12 @@ int main(int argc, char** argv)
catch (const toml::parse_error& err)
{
std::cerr
<< "Error parsing file '"sv << *err.where().path
<< "':\n"sv << err.what()
<< "\n ("sv << err.where().begin << ")"sv
<< "Error parsing file '"sv << *err.source().path
<< "':\n"sv << err.description()
<< "\n ("sv << err.source().begin << ")"sv
<< std::endl;
return 1;
}
return 0;
}

View File

@ -32,9 +32,9 @@ int main(int argc, char** argv)
catch (const toml::parse_error & err)
{
std::cerr
<< "Error parsing file '"sv << *err.where().path
<< "':\n"sv << err.what()
<< "\n ("sv << err.where().begin << ")"sv
<< "Error parsing file '"sv << *err.source().path
<< "':\n"sv << err.description()
<< "\n ("sv << err.source().begin << ")"sv
<< std::endl;
return 1;
@ -52,8 +52,8 @@ int main(int argc, char** argv)
catch (const toml::parse_error& err)
{
std::cerr
<< "Error parsing stdin:\n"sv << err.what()
<< "\n ("sv << err.where().begin << ")"sv
<< "Error parsing stdin:\n"sv << err.description()
<< "\n ("sv << err.source().begin << ")"sv
<< std::endl;
return 1;

View File

@ -5,14 +5,15 @@
//# is used as the source for generate_single_header.py.
#include "toml_common.h"
#include "toml_print_to_stream.h"
#include "toml_date_time.h"
#include "toml_node.h"
#include "toml_table.h"
#include "toml_array.h"
#include "toml_value.h"
#include "toml_utf8.h"
#include "toml_node_view.h"
#include "toml_utf8.h"
#include "toml_parser.h"
#include "toml_print_to_stream.h"
#include "toml_formatter.h"
#include "toml_default_formatter.h"
#include "toml_json_formatter.h"

View File

@ -27,10 +27,10 @@ namespace toml::impl
public:
array_iterator() noexcept = default;
using reference = std::conditional_t<is_const, const node&, node&>;
array_iterator() noexcept = default;
array_iterator& operator++() noexcept // ++pre
{
++raw_;

View File

@ -114,6 +114,8 @@
#define TOML_USE_STREAMS_FOR_FLOATS 1
#endif
#elif defined (DOXYGEN)
#define TOML_EXCEPTIONS 0
#endif
#ifndef TOML_CPP_VERSION
@ -268,19 +270,23 @@ TOML_POP_WARNINGS
////////// FORWARD DECLARATIONS & TYPEDEFS
// clang-format on
/// \brief The root namespace for all toml++ functions and types.
namespace toml
{
/// \brief User-defined literals.
inline namespace literals
{
using namespace std::string_literals;
using namespace std::string_view_literals;
/// \brief Specifies a uint8_t literal.
[[nodiscard]] TOML_ALWAYS_INLINE
TOML_CONSTEVAL uint8_t operator"" _u8(unsigned long long n) noexcept
{
return static_cast<uint8_t>(n);
}
/// \brief Specifies a size_t literal.
[[nodiscard]] TOML_ALWAYS_INLINE
TOML_CONSTEVAL size_t operator"" _sz(unsigned long long n) noexcept
{
@ -296,14 +302,24 @@ namespace toml
#else
/// \brief The base character type for toml++ keys and string values.
/// \remarks This will be `char8_t` if `TOML_CHAR_8_STRINGS = 1`, otherwise it will be `char`.
using string_char = char;
/// \brief The string type for toml++ keys and string values.
/// \remarks This will be `std::u8string` if `TOML_CHAR_8_STRINGS = 1`, otherwise it will be `std::string`.
using string = std::string;
/// \brief The string type for toml++ keys and string values.
/// \remarks This will be `std::u8string_view` if `TOML_CHAR_8_STRINGS = 1`, otherwise it will be `std::string_view`.
using string_view = std::string_view;
#endif
template <typename T>
using string_map = std::map<string, T, std::less<>>; //heterogeneous lookup
struct date;
struct time;
struct time_offset;
struct date_time;
class node;
template <typename T> class node_view;
@ -311,6 +327,7 @@ namespace toml
class array;
class table;
/// \brief TOML node type identifiers.
enum class node_type : uint8_t
{
table,
@ -330,21 +347,31 @@ namespace toml
#else
/// \brief The integer type used to tally line numbers and columns.
/// \remarks This will be `uint32_t` if `TOML_LARGE_FILES = 1`, otherwise it will be `uint16_t`.
using source_index = uint16_t;
#endif
/// \brief A source document line-and-column pair.
struct source_position final
{
source_index line; //begins at 1
source_index column; //begins at 1
/// \brief The line number.
/// \remarks Valid line numbers start at 1.
source_index line;
/// \brief The column number.
/// \remarks Valid column numbers start at 1.
source_index column;
/// \brief Returns true if both line and column numbers are non-zero.
[[nodiscard]]
explicit constexpr operator bool () const noexcept
{
return line > source_index{} && column > source_index{};
}
[[nodiscard]]
friend constexpr bool operator == (const source_position& lhs, const source_position& rhs) noexcept
{
@ -381,12 +408,21 @@ namespace toml
}
};
/// \brief A pointer to a shared string resource containing a source path.
using source_path_ptr = std::shared_ptr<const std::string>;
/// \brief A source document region.
struct source_region final
{
/// \brief The beginning of the region (inclusive).
source_position begin;
/// \brief The end of the region (exclusive).
source_position end;
/// \brief The path to the corresponding source document.
///
/// \remarks This will be `nullptr` if no path was provided to toml::parse().
source_path_ptr path;
};
@ -404,23 +440,29 @@ namespace toml
public:
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
parse_error(const char* description, source_region&& source) noexcept
: std::runtime_error{ description },
source_{ std::move(source) }
parse_error(const char* desc, source_region&& src) noexcept
: std::runtime_error{ desc },
source_{ std::move(src) }
{}
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
parse_error(const char* description, const source_region& source) noexcept
: parse_error{ description, source_region{ source } }
parse_error(const char* desc, const source_region& src) noexcept
: parse_error{ desc, source_region{ src } }
{}
TOML_NODISCARD_CTOR TOML_GCC_ATTR(nonnull)
parse_error(const char* description, const source_position& position, const source_path_ptr& path = {}) noexcept
: parse_error{ description, source_region{ position, position, path } }
parse_error(const char* desc, const source_position& position, const source_path_ptr& path = {}) noexcept
: parse_error{ desc, source_region{ position, position, path } }
{}
[[nodiscard]]
const source_region& where() const noexcept
std::string_view description() const noexcept
{
return std::string_view{ what() };
}
[[nodiscard]]
const source_region& source() const noexcept
{
return source_;
}
@ -428,6 +470,10 @@ namespace toml
#else
/// \brief An error thrown/returned when parsing fails.
///
/// \remarks This will inherit from `std::runtime_exception` when `TOML_EXCEPTIONS = 1`.
/// The public interface will be exactly the same either way.
class parse_error final
{
private:
@ -437,29 +483,32 @@ namespace toml
public:
TOML_NODISCARD_CTOR
parse_error(std::string&& description, source_region&& source) noexcept
: description_{ std::move(description) },
source_{ std::move(source) }
parse_error(std::string&& desc, source_region&& src) noexcept
: description_{ std::move(desc) },
source_{ std::move(src) }
{}
TOML_NODISCARD_CTOR
parse_error(std::string&& description, const source_region& source) noexcept
: parse_error{ std::move(description), source_region{ source } }
parse_error(std::string&& desc, const source_region& src) noexcept
: parse_error{ std::move(desc), source_region{ src } }
{}
TOML_NODISCARD_CTOR
parse_error(std::string&& description, const source_position& position, const source_path_ptr& path = {}) noexcept
: parse_error{ std::move(description), source_region{ position, position, path } }
parse_error(std::string&& desc, const source_position& position, const source_path_ptr& path = {}) noexcept
: parse_error{ std::move(desc), source_region{ position, position, path } }
{}
/// \brief Returns a textual description of the error.
[[nodiscard]]
std::string_view what() const noexcept
std::string_view description() const noexcept
{
return description_;
}
/// \brief Returns the region of the source document responsible for the error.
[[nodiscard]]
const source_region& where() const noexcept
const source_region& source() const noexcept
{
return source_;
}
@ -468,103 +517,14 @@ namespace toml
#endif
TOML_POP_WARNINGS
struct date final
{
uint16_t year;
uint8_t month;
uint8_t day;
[[nodiscard]]
friend constexpr bool operator == (date lhs, date rhs) noexcept
{
return lhs.year == rhs.year
&& lhs.month == rhs.month
&& lhs.day == rhs.day;
}
[[nodiscard]]
friend constexpr bool operator != (date lhs, date rhs) noexcept
{
return lhs.year != rhs.year
|| lhs.month != rhs.month
|| lhs.day != rhs.day;
}
};
struct time final
{
uint8_t hour;
uint8_t minute;
uint8_t second;
uint32_t nanosecond;
[[nodiscard]]
friend constexpr bool operator == (const time& lhs, const time& rhs) noexcept
{
return lhs.hour == rhs.hour
&& lhs.minute == rhs.minute
&& lhs.second == rhs.second
&& lhs.nanosecond == rhs.nanosecond;
}
[[nodiscard]]
friend constexpr bool operator != (const time& lhs, const time& rhs) noexcept
{
return !(lhs == rhs);
}
};
struct time_offset final
{
int16_t minutes;
[[nodiscard]]
static constexpr time_offset from_hh_mm(int8_t hours, int8_t minutes) noexcept
{
return time_offset{ static_cast<int16_t>(hours * 60 + minutes) };
}
[[nodiscard]]
friend constexpr bool operator == (time_offset lhs, time_offset rhs) noexcept
{
return lhs.minutes == rhs.minutes;
}
[[nodiscard]]
friend constexpr bool operator != (time_offset lhs, time_offset rhs) noexcept
{
return lhs.minutes != rhs.minutes;
}
};
struct date_time final
{
toml::date date;
toml::time time;
std::optional<toml::time_offset> time_offset;
[[nodiscard]]
friend constexpr bool operator == (const date_time& lhs, const date_time& rhs) noexcept
{
return lhs.date == rhs.date
&& lhs.time == rhs.time
&& lhs.time_offset == rhs.time_offset;
}
[[nodiscard]]
friend constexpr bool operator != (const date_time& lhs, const date_time& rhs) noexcept
{
return lhs.date != rhs.date
|| lhs.time != rhs.time
|| lhs.time_offset != rhs.time_offset;
}
};
}
/// \brief Internal implementation details. No user-serviceable parts within.
namespace toml::impl
{
template <typename T>
using string_map = std::map<string, T, std::less<>>; //heterogeneous lookup
#if defined(__cpp_lib_remove_cvref) || (defined(_MSC_VER) && defined(_HAS_CXX20))
template <typename T>
@ -656,6 +616,7 @@ namespace toml::impl
template <> struct value_promoter<uint16_t> { using type = int64_t; };
template <> struct value_promoter<uint8_t> { using type = int64_t; };
template <> struct value_promoter<float> { using type = double; };
template <typename T> using promoted = typename impl::value_promoter<T>::type;
inline constexpr toml::string_view low_character_escape_table[] =
{
@ -705,6 +666,28 @@ namespace toml::impl
"time"sv,
"date-time"sv
};
#define TOML_P2S_DECL(linkage, type) \
template <typename CHAR> \
linkage void print_to_stream(type, std::basic_ostream<CHAR>&) TOML_MAY_THROW
TOML_P2S_DECL(TOML_ALWAYS_INLINE, int8_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, int16_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, int32_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, int64_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, uint8_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, uint16_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, uint32_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, uint64_t);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, float);
TOML_P2S_DECL(TOML_ALWAYS_INLINE, double);
TOML_P2S_DECL(inline, const date&);
TOML_P2S_DECL(inline, const time&);
TOML_P2S_DECL(inline, time_offset);
TOML_P2S_DECL(inline, const date_time&);
#undef TOML_P2S_DECL
}
namespace toml
@ -715,9 +698,6 @@ namespace toml
template <typename T>
using value_of = typename impl::node_unwrapper<T>::type;
template <typename T>
using promoted = typename impl::value_promoter<T>::type;
template <typename CHAR>
inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, node_type rhs) TOML_MAY_THROW
{

View File

@ -0,0 +1,135 @@
#pragma once
#include "toml_common.h"
namespace toml
{
struct date final
{
uint16_t year;
uint8_t month;
uint8_t day;
[[nodiscard]]
friend constexpr bool operator == (date lhs, date rhs) noexcept
{
return lhs.year == rhs.year
&& lhs.month == rhs.month
&& lhs.day == rhs.day;
}
[[nodiscard]]
friend constexpr bool operator != (date lhs, date rhs) noexcept
{
return lhs.year != rhs.year
|| lhs.month != rhs.month
|| lhs.day != rhs.day;
}
template <typename CHAR>
friend inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const date& rhs)
TOML_MAY_THROW
{
impl::print_to_stream(rhs, lhs);
return lhs;
}
};
struct time final
{
uint8_t hour;
uint8_t minute;
uint8_t second;
uint32_t nanosecond;
[[nodiscard]]
friend constexpr bool operator == (const time& lhs, const time& rhs) noexcept
{
return lhs.hour == rhs.hour
&& lhs.minute == rhs.minute
&& lhs.second == rhs.second
&& lhs.nanosecond == rhs.nanosecond;
}
[[nodiscard]]
friend constexpr bool operator != (const time& lhs, const time& rhs) noexcept
{
return !(lhs == rhs);
}
template <typename CHAR>
friend inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const time& rhs)
TOML_MAY_THROW
{
impl::print_to_stream(rhs, lhs);
return lhs;
}
};
struct time_offset final
{
int16_t minutes;
[[nodiscard]]
static constexpr time_offset from_hh_mm(int8_t hours, int8_t minutes) noexcept
{
return time_offset{ static_cast<int16_t>(hours * 60 + minutes) };
}
[[nodiscard]]
friend constexpr bool operator == (time_offset lhs, time_offset rhs) noexcept
{
return lhs.minutes == rhs.minutes;
}
[[nodiscard]]
friend constexpr bool operator != (time_offset lhs, time_offset rhs) noexcept
{
return lhs.minutes != rhs.minutes;
}
template <typename CHAR>
friend inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const time_offset& rhs)
TOML_MAY_THROW
{
impl::print_to_stream(rhs, lhs);
return lhs;
}
};
struct date_time final
{
toml::date date;
toml::time time;
std::optional<toml::time_offset> time_offset;
[[nodiscard]]
constexpr bool is_local() const noexcept
{
return !time_offset.has_value();
}
[[nodiscard]]
friend constexpr bool operator == (const date_time& lhs, const date_time& rhs) noexcept
{
return lhs.date == rhs.date
&& lhs.time == rhs.time
&& lhs.time_offset == rhs.time_offset;
}
[[nodiscard]]
friend constexpr bool operator != (const date_time& lhs, const date_time& rhs) noexcept
{
return lhs.date != rhs.date
|| lhs.time != rhs.time
|| lhs.time_offset != rhs.time_offset;
}
template <typename CHAR>
friend inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const date_time& rhs)
TOML_MAY_THROW
{
impl::print_to_stream(rhs, lhs);
return lhs;
}
};
}

View File

@ -6,6 +6,7 @@
namespace toml::impl
{
[[nodiscard]]
inline toml::string default_formatter_make_key_segment(const toml::string& str) noexcept
{
if (str.empty())
@ -40,6 +41,7 @@ namespace toml::impl
}
}
[[nodiscard]]
inline size_t default_formatter_inline_columns(const node& node) noexcept
{
return node.visit([](const auto& n) noexcept
@ -110,6 +112,7 @@ namespace toml::impl
});
}
[[nodiscard]]
inline bool default_formatter_forces_multiline(const node& node, size_t starting_column_bias = 0) noexcept
{
return (default_formatter_inline_columns(node) + starting_column_bias) > 120_sz;

View File

@ -1,5 +1,5 @@
#pragma once
#include "toml_value.h"
#include "toml_print_to_stream.h"
namespace toml::impl
{
@ -8,6 +8,7 @@ namespace toml::impl
// TINAE - char can have signed _or_ unsigned semantics and I can't
// be arsed handling this differently
[[nodiscard]]
inline toml::string_view escape_string_character(const toml::string_char& c) noexcept
{
if (c >= TOML_STRING_PREFIX('\x00') && c <= TOML_STRING_PREFIX('\x1F')) TOML_UNLIKELY
@ -40,17 +41,16 @@ namespace toml::impl
size_t indent_columns_;
protected:
[[nodiscard]] const toml::table& source() const noexcept { return source_; }
[[nodiscard]] const formatter_options& options() const noexcept { return options_; }
[[nodiscard]] std::basic_ostream<CHAR>& stream() const noexcept { return *stream_; }
[[nodiscard]] int indent() const noexcept { return indent_; }
[[nodiscard]] size_t indent_columns() const noexcept { return indent_columns_; }
void indent(int level) noexcept { indent_ = level; }
void increase_indent() noexcept { indent_++; }
void decrease_indent() noexcept { indent_--; }
size_t indent_columns() const noexcept { return indent_columns_; }
void clear_naked_newline() noexcept { naked_newline_ = false; }

View File

@ -35,11 +35,12 @@ namespace toml
return reinterpret_cast<const node_of<T>*>(this);
}
public:
node() noexcept = default;
node(const node&) = delete;
node& operator= (const node&) = delete;
public:
virtual ~node() noexcept = default;
[[nodiscard]] virtual node_type type() const noexcept = 0;

View File

@ -199,7 +199,7 @@ namespace toml
template <typename U>
[[nodiscard]] static bool value_equality(const node_view& lhs, const U& rhs) noexcept
{
const auto val = lhs.as<promoted<U>>();
const auto val = lhs.as<impl::promoted<U>>();
return val && val->get() == rhs;
}
@ -222,7 +222,7 @@ namespace toml
size_t i{};
for (auto& list_elem : rhs)
{
const auto elem = arr->get_as<promoted<elem_t>>(i++);
const auto elem = arr->get_as<impl::promoted<elem_t>>(i++);
if (!elem || elem->get() != list_elem)
return false;
}
@ -282,10 +282,6 @@ namespace toml
return { this, key };
}
// inline constexpr auto kek1 = sizeof(node_view<table>);
// inline constexpr auto kek2 = sizeof(decltype(std::declval<node_view<table>>()[0]));
// inline constexpr auto kek3 = sizeof(decltype(std::declval<node_view<table>>()["kek"sv]));
inline node_view<const table> table::operator[] (string_view key) const noexcept
{
return { this, key };

View File

@ -1,8 +1,8 @@
#pragma once
#include "toml_utf8.h"
#include "toml_value.h"
#include "toml_array.h"
#include "toml_table.h"
#include "toml_array.h"
#include "toml_value.h"
namespace toml
{
@ -146,6 +146,48 @@ namespace toml::impl
#define TOML_ERROR err.emplace
#endif
#if !TOML_EXCEPTIONS || defined(__INTELLISENSE__)
#define TOML_NORETURN
#else
#define TOML_NORETURN [[noreturn]]
#endif
template <int> struct parse_integer_traits;
template <> struct parse_integer_traits<2> final
{
static constexpr auto qualifier = "binary"sv;
static constexpr auto is_digit = is_binary_digit;
static constexpr auto is_signed = false;
static constexpr auto buffer_length = 63;
static constexpr char32_t prefix_codepoint = U'b';
static constexpr char prefix = 'b';
};
template <> struct parse_integer_traits<8> final
{
static constexpr auto qualifier = "octal"sv;
static constexpr auto is_digit = is_octal_digit;
static constexpr auto is_signed = false;
static constexpr auto buffer_length = 21; // strlen("777777777777777777777")
static constexpr char32_t prefix_codepoint = U'o';
static constexpr char prefix = 'o';
};
template <> struct parse_integer_traits<10> final
{
static constexpr auto qualifier = "decimal"sv;
static constexpr auto is_digit = is_decimal_digit;
static constexpr auto is_signed = true;
static constexpr auto buffer_length = 19; //strlen("9223372036854775807")
};
template <> struct parse_integer_traits<16> final
{
static constexpr auto qualifier = "hexadecimal"sv;
static constexpr auto is_digit = is_hexadecimal_digit;
static constexpr auto is_signed = false;
static constexpr auto buffer_length = 16; //strlen("7FFFFFFFFFFFFFFF")
static constexpr char32_t prefix_codepoint = U'x';
static constexpr char prefix = 'x';
};
class parser final
{
private:
@ -171,9 +213,7 @@ namespace toml::impl
}
template <typename... T>
#if TOML_EXCEPTIONS
[[noreturn]]
#endif
TOML_NORETURN
void abort_with_error(T &&... args) const TOML_MAY_THROW
{
TOML_ERROR_CHECK();
@ -541,7 +581,7 @@ namespace toml::impl
{
eof_check();
if (!is_hex_digit(*cp))
if (!is_hexadecimal_digit(*cp))
abort_with_error(
"Encountered unexpected character while parsing "sv,
(MULTI_LINE ? "multi-line "sv : ""sv),
@ -1159,7 +1199,7 @@ namespace toml::impl
if (*cp == U'_')
{
if (!prev || !is_hex_digit(*prev))
if (!prev || !is_hexadecimal_digit(*prev))
abort_with_error(
"Encountered unexpected character while parsing hexadecimal "sv,
node_type::floating_point, "; underscores may only follow digits"sv
@ -1214,7 +1254,7 @@ namespace toml::impl
}
else
{
if (!seen_exponent && !is_hex_digit(*cp))
if (!seen_exponent && !is_hexadecimal_digit(*cp))
abort_with_error("Encountered unexpected character while parsing hexadecimal "sv,
node_type::floating_point, "; expected hexadecimal digit, saw '"sv, *cp, '\''
);
@ -1287,41 +1327,73 @@ namespace toml::impl
#endif //!TOML_USE_STREAMS_FOR_FLOATS && TOML_LANG_HIGHER_THAN(0, 5, 0)
template <int base>
[[nodiscard]]
int64_t parse_binary_integer() TOML_MAY_THROW
int64_t parse_integer() TOML_MAY_THROW
{
TOML_ERROR_CHECK({});
TOML_ASSERT(cp && *cp == U'0');
TOML_ASSERT(cp);
using traits = parse_integer_traits<base>;
const auto eof_check = [this]() TOML_MAY_THROW
{
TOML_ERROR_CHECK();
if (!cp)
abort_with_error("Encountered EOF while parsing binary "sv, node_type::integer);
abort_with_error(
"Encountered EOF while parsing "sv, traits::qualifier, ' ', node_type::integer
);
};
// '0'
if (*cp != U'0')
abort_with_error(
"Encountered unexpected character while parsing binary "sv,
node_type::integer, "; expected '0', saw '"sv, *cp, '\''
);
advance();
eof_check();
[[maybe_unused]] int64_t sign = 1;
if constexpr (traits::is_signed)
{
if (*cp == U'-')
{
sign = -1;
advance();
}
else if(*cp == U'+')
advance();
eof_check();
// 'b' or 'B'
if (*cp != U'b' && *cp != U'B')
abort_with_error(
"Encountered unexpected character while parsing binary "sv,
node_type::integer, "; expected 'b' or 'B', saw '"sv, *cp, '\''
);
advance();
eof_check();
TOML_ERROR_CHECK({});
}
if constexpr (base == 10)
{
if (!traits::is_digit(*cp))
abort_with_error(
"Encountered unexpected character while parsing "sv, traits::qualifier, ' ',
node_type::integer, "; expected expected "sv, traits::qualifier,
" digit or sign, saw '"sv, *cp, '\''
);
}
else
{
// '0'
if (*cp != U'0')
abort_with_error(
"Encountered unexpected character while parsing "sv, traits::qualifier,
' ', node_type::integer, "; expected '0', saw '"sv, *cp, '\''
);
advance();
eof_check();
// 'b', 'o', 'x'
if (*cp != traits::prefix_codepoint)
abort_with_error(
"Encountered unexpected character while parsing "sv, traits::qualifier,
' ', node_type::integer, "; expected '"sv, traits::prefix,
"', saw '"sv, *cp, '\''
);
advance();
eof_check();
}
TOML_ERROR_CHECK({});
// consume value chars
TOML_GCC_ATTR(uninitialized) char chars[64];
TOML_GCC_ATTR(uninitialized) char chars[traits::buffer_length];
size_t length = {};
const utf8_codepoint* prev = {};
while (true)
@ -1331,22 +1403,23 @@ namespace toml::impl
if (*cp == U'_')
{
if (!prev || !is_binary_digit(*prev))
if (!prev || !traits::is_digit(*prev))
abort_with_error(
"Encountered unexpected character while parsing binary "sv,
node_type::integer, "; underscores may only follow digits"sv
"Encountered unexpected character while parsing "sv, traits::qualifier,
' ', node_type::integer, "; expected "sv, traits::qualifier, " digit, saw '_'"sv
);
}
else
{
if (!is_binary_digit(*cp))
if (!traits::is_digit(*cp))
abort_with_error(
"Encountered unexpected character while parsing binary "sv,
node_type::integer, "; expected binary digit, saw '"sv, *cp, '\''
"Encountered unexpected character while parsing "sv, traits::qualifier,
' ', node_type::integer, "; expected "sv, traits::qualifier,
" digit, saw '"sv, *cp, '\''
);
if (length == sizeof(chars))
abort_with_error(
"Error parsing binary "sv, node_type::integer,
"Error parsing "sv, traits::qualifier, ' ', node_type::integer,
"; exceeds maximum length of "sv, sizeof(chars), " characters"sv
);
chars[length++] = static_cast<char>(cp->bytes[0]);
@ -1360,399 +1433,84 @@ namespace toml::impl
{
eof_check();
abort_with_error(
"Encountered unexpected character while parsing binary "sv,
node_type::integer, "; expected binary digit, saw '"sv, *cp, '\''
"Encountered unexpected character while parsing "sv, traits::qualifier,
' ', node_type::integer, "; expected "sv, traits::qualifier, " digit, saw '_'"sv
);
}
// check for leading zeroes
if constexpr (base == 10)
{
if (chars[0] == '0')
abort_with_error(
"Error parsing "sv, traits::qualifier,
' ', node_type::integer, "; leading zeroes are not allowed"sv
);
}
TOML_ERROR_CHECK({});
// single digits can be converted directly
if (length == 1_sz)
return chars[0] == '1' ? 1ull : 0ull;
TOML_ERROR_CHECK({});
// otherwise invoke charconv
TOML_GCC_ATTR(uninitialized) int64_t result;
auto parse_result = std::from_chars(chars, chars + length, result, 2);
switch (parse_result.ec)
{
case std::errc{}: //ok
return result;
case std::errc::invalid_argument:
abort_with_error(
"Error parsing binary "sv, node_type::integer,
"; '"sv, std::string_view{ chars, length }, "' could not be interpreted as a value"sv
);
break;
case std::errc::result_out_of_range:
abort_with_error(
"Error parsing binary "sv, node_type::integer,
"; '"sv, std::string_view{ chars, length }, "' is not representable in 64 bits"sv
);
break;
default: //??
abort_with_error(
"Error parsing binary "sv, node_type::integer,
"; an unspecified error occurred while trying to interpret '",
std::string_view{ chars, length }, "' as a value"sv
);
}
TOML_ERROR_CHECK({});
TOML_UNREACHABLE;
}
[[nodiscard]]
int64_t parse_octal_integer() TOML_MAY_THROW
{
TOML_ERROR_CHECK({});
TOML_ASSERT(cp && *cp == U'0');
const auto eof_check = [this]() TOML_MAY_THROW
{
TOML_ERROR_CHECK();
if (!cp)
abort_with_error("Encountered EOF while parsing octal "sv, node_type::integer);
};
// '0'
if (*cp != U'0')
abort_with_error(
"Encountered unexpected character while parsing octal "sv, node_type::integer,
"; expected '0', saw '"sv, *cp, '\''
);
advance();
eof_check();
// 'o'
if (*cp != U'o')
abort_with_error(
"Encountered unexpected character while parsing octal "sv, node_type::integer,
"; expected 'o', saw '"sv, *cp, '\''
);
advance();
eof_check();
TOML_ERROR_CHECK({});
// consume value chars
TOML_GCC_ATTR(uninitialized) char chars[21]; //21 == strlen("777777777777777777777") (max 64-bit uint)
size_t length = {};
const utf8_codepoint* prev = {};
while (true)
{
if (!cp || is_value_terminator(*cp))
break;
if (*cp == U'_')
if constexpr (base > 10)
{
if (!prev || !is_octal_digit(*prev))
abort_with_error(
"Encountered unexpected character while parsing octal "sv, node_type::integer,
"; underscores may only follow digits"sv
);
return chars[0] >= 'A'
? 10LL + static_cast<int64_t>(*cp - (*cp >= 'a' ? 'a' : 'A'))
: static_cast<int64_t>(*cp - '0');
}
else
{
if (!is_octal_digit(*cp))
abort_with_error(
"Encountered unexpected character while parsing octal "sv, node_type::integer,
"; expected octal digit, saw '"sv, *cp, '\''
);
if (length == sizeof(chars))
abort_with_error(
"Error parsing octal "sv, node_type::integer,
"; exceeds maximum length of "sv, sizeof(chars), " characters"sv
);
chars[length++] = static_cast<char>(cp->bytes[0]);
}
prev = cp;
advance();
TOML_ERROR_CHECK({});
return static_cast<int64_t>(chars[0] - '0');
}
if (prev && *prev == U'_')
{
eof_check();
abort_with_error(
"Encountered unexpected character while parsing octal "sv, node_type::integer,
"; expected octal digit, saw '"sv, *cp, '\''
);
}
// single digits can be converted directly
if (length == 1_sz)
return static_cast<int64_t>(chars[0] - '0');
TOML_ERROR_CHECK({});
// otherwise invoke charconv
TOML_GCC_ATTR(uninitialized) int64_t result;
auto parse_result = std::from_chars(chars, chars + length, result, 8);
switch (parse_result.ec)
{
case std::errc{}: //ok
return result;
case std::errc::invalid_argument:
abort_with_error(
"Error parsing octal "sv, node_type::integer,
"; '"sv, std::string_view{ chars, length }, "' could not be interpreted as a value"sv
);
break;
case std::errc::result_out_of_range:
abort_with_error(
"Error parsing octal "sv, node_type::integer,
"; '"sv, std::string_view{ chars, length }, "' is not representable in 64 bits"sv
);
break;
default: //??
abort_with_error(
"Error parsing octal "sv, node_type::integer,
"; an unspecified error occurred while trying to interpret '",
std::string_view{ chars, length }, "' as a value"sv
);
}
TOML_ERROR_CHECK({});
TOML_UNREACHABLE;
}
[[nodiscard]]
int64_t parse_decimal_integer() TOML_MAY_THROW
{
TOML_ERROR_CHECK({});
TOML_ASSERT(cp && (*cp == U'+' || *cp == U'-' || is_decimal_digit(*cp)));
const auto eof_check = [this]() TOML_MAY_THROW
{
TOML_ERROR_CHECK();
if (!cp)
abort_with_error("Encountered EOF while parsing "sv, node_type::integer);
};
// sign
const int64_t sign = *cp == U'-' ? -1ll : 1ll;
if (*cp == U'+' || *cp == U'-')
{
advance();
eof_check();
}
TOML_ERROR_CHECK({});
// consume value chars
TOML_GCC_ATTR(uninitialized) char chars[19]; //19 == strlen("9223372036854775807") (max 64-bit uint)
size_t length = {};
const utf8_codepoint* prev = {};
while (true)
{
if (!cp || is_value_terminator(*cp))
break;
if (*cp == U'_')
{
if (!prev || !is_decimal_digit(*prev))
abort_with_error(
"Encountered unexpected character while parsing "sv, node_type::integer,
"; underscores may only follow digits"sv
);
}
else
{
if (!is_decimal_digit(*cp))
abort_with_error(
"Encountered unexpected character while parsing "sv, node_type::integer,
"; expected decimal digit, saw '"sv, *cp, '\''
);
if (length == sizeof(chars))
abort_with_error(
"Error parsing "sv, node_type::integer,
"; exceeds maximum length of "sv, sizeof(chars), " characters"sv
);
chars[length++] = static_cast<char>(cp->bytes[0]);
}
prev = cp;
advance();
TOML_ERROR_CHECK({});
}
if (prev && *prev == U'_')
{
eof_check();
abort_with_error(
"Encountered unexpected character while parsing "sv, node_type::integer,
"; expected decimal digit, saw '"sv, *cp, '\''
);
}
// check for leading zeroes etc
if (chars[0] == '0')
abort_with_error(
"Encountered unexpected character while parsing "sv, node_type::integer,
"; leading zeroes are not allowed"sv
);
TOML_ERROR_CHECK({});
// convert to int
TOML_GCC_ATTR(uninitialized) uint64_t result;
auto parse_result = std::from_chars(chars, chars + length, result);
if (parse_result.ec == std::errc{} && (
(sign < 0 && result > static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1ull)
|| (sign > 0 && result > static_cast<uint64_t>(std::numeric_limits<int64_t>::max()))
))
parse_result.ec = std::errc::result_out_of_range;
auto parse_result = std::from_chars(chars, chars + length, result, base);
if constexpr (traits::is_signed)
{
if (parse_result.ec == std::errc{} && (
(sign < 0 && result > static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1ull)
|| (sign > 0 && result > static_cast<uint64_t>(std::numeric_limits<int64_t>::max()))
))
parse_result.ec = std::errc::result_out_of_range;
}
else
{
if (parse_result.ec == std::errc{} &&
result > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())
)
parse_result.ec = std::errc::result_out_of_range;
}
switch (parse_result.ec)
{
case std::errc{}: //ok
return static_cast<int64_t>(result) * sign;
if constexpr (traits::is_signed)
return static_cast<int64_t>(result) * sign;
else
return static_cast<int64_t>(result);
case std::errc::invalid_argument:
abort_with_error(
"Error parsing "sv, node_type::integer,
"Error parsing "sv, traits::qualifier, ' ', node_type::integer,
"; '"sv, std::string_view{ chars, length }, "' could not be interpreted as a value"sv
);
break;
case std::errc::result_out_of_range:
abort_with_error(
"Error parsing "sv, node_type::integer,
"Error parsing "sv, traits::qualifier, ' ', node_type::integer,
"; '"sv, std::string_view{ chars, length }, "' is not representable in 64 bits"sv
);
break;
default: //??
abort_with_error(
"Error parsing "sv, node_type::integer,
"Error parsing "sv, traits::qualifier, ' ', node_type::integer,
"; an unspecified error occurred while trying to interpret '",
std::string_view{ chars, length }, "' as a value"sv
);
}
TOML_ERROR_CHECK({});
TOML_UNREACHABLE;
}
[[nodiscard]]
int64_t parse_hex_integer() TOML_MAY_THROW
{
TOML_ERROR_CHECK({});
TOML_ASSERT(cp && *cp == U'0');
const auto eof_check = [this]() TOML_MAY_THROW
{
TOML_ERROR_CHECK();
if (!cp)
abort_with_error("Encountered EOF while parsing hexadecimal "sv, node_type::integer);
};
// '0'
if (*cp != U'0')
abort_with_error(
"Encountered unexpected character while parsing hexadecimal "sv, node_type::integer,
"; expected '0', saw '"sv, *cp, '\''
);
advance();
eof_check();
// 'x' or 'X'
if (*cp != U'x' && *cp != U'X')
abort_with_error(
"Encountered unexpected character while parsing hexadecimal "sv, node_type::integer,
"; expected 'x' or 'X', saw '"sv, *cp, '\''
);
advance();
eof_check();
TOML_ERROR_CHECK({});
// consume value chars
TOML_GCC_ATTR(uninitialized) char chars[16]; //16 == strlen("FFFFFFFFFFFFFFFF") (max 64-bit uint)
size_t length = {};
const utf8_codepoint* prev = {};
while (true)
{
if (!cp || is_value_terminator(*cp))
break;
if (*cp == U'_')
{
if (!prev || !is_hex_digit(*prev))
abort_with_error(
"Encountered unexpected character while parsing hexadecimal "sv, node_type::integer,
"; underscores may only follow digits"
);
}
else
{
if (!is_hex_digit(*cp))
abort_with_error(
"Encountered unexpected character while parsing hexadecimal "sv, node_type::integer,
"; expected hexadecimal digit, saw '"sv, *cp, '\''
);
if (length == sizeof(chars))
abort_with_error(
"Error parsing hexadecimal "sv, node_type::integer,
"; exceeds maximum length of "sv, sizeof(chars), " characters"sv
);
chars[length++] = static_cast<char>(cp->bytes[0]);
}
prev = cp;
advance();
TOML_ERROR_CHECK({});
}
if (prev && *prev == U'_')
{
eof_check();
abort_with_error(
"Encountered unexpected character while parsing hexadecimal "sv, node_type::integer,
"; expected hexadecimal digit, saw '"sv, *cp, '\''
);
}
// single digits can be converted directly
if (length == 1_sz)
return chars[0] >= 'A'
? static_cast<int64_t>(10 + (chars[0] - (chars[0] >= 'a' ? 'a' : 'A')))
: static_cast<int64_t>(chars[0] - '0');
TOML_ERROR_CHECK({});
// otherwise invoke charconv
TOML_GCC_ATTR(uninitialized) int64_t result;
auto parse_result = std::from_chars(chars, chars + length, result, 16);
switch (parse_result.ec)
{
case std::errc{}: //ok
return result;
case std::errc::invalid_argument:
abort_with_error(
"Error parsing hexadecimal "sv, node_type::integer,
"; '"sv, std::string_view{ chars, length }, "' could not be interpreted as a value"sv
);
break;
case std::errc::result_out_of_range:
abort_with_error(
"Error parsing hexadecimal "sv, node_type::integer,
"; '"sv, std::string_view{ chars, length }, "' is not representable in 64 bits"sv
);
break;
default: //??
abort_with_error(
"Error parsing hexadecimal "sv, node_type::integer,
"; an unspecified error occurred while trying to interpret '",
std::string_view{ chars, length }, "' as a value"sv
);
}
TOML_ERROR_CHECK({});
TOML_UNREACHABLE;
}
@ -2359,8 +2117,8 @@ namespace toml::impl
case U'E': [[fallthrough]];
case U'e': [[fallthrough]];
case U'.': val = std::make_unique<value<double>>(parse_float()); break;
case U'b': val = std::make_unique<value<int64_t>>(parse_binary_integer()); break;
case U'o': val = std::make_unique<value<int64_t>>(parse_octal_integer()); break;
case U'b': val = std::make_unique<value<int64_t>>(parse_integer<2>()); break;
case U'o': val = std::make_unique<value<int64_t>>(parse_integer<8>()); break;
case U'X': [[fallthrough]];
case U'x':
{
@ -2393,7 +2151,7 @@ namespace toml::impl
if (val)
break;
val = std::make_unique<value<int64_t>>(parse_hex_integer());
val = std::make_unique<value<int64_t>>(parse_integer<16>());
break;
}
}
@ -2410,7 +2168,7 @@ namespace toml::impl
{
// 100
case has_digits:
val = std::make_unique<value<int64_t>>(parse_decimal_integer());
val = std::make_unique<value<int64_t>>(parse_integer<10>());
break;
// 1e1
@ -2491,7 +2249,7 @@ namespace toml::impl
// -100
case has_digits | has_minus: [[fallthrough]];
case has_digits | has_plus:
val = std::make_unique<value<int64_t>>(parse_decimal_integer());
val = std::make_unique<value<int64_t>>(parse_integer<10>());
break;
// +1e1
@ -3256,6 +3014,7 @@ namespace toml::impl
#undef TOML_ERROR_CHECK
#undef TOML_ERROR
#undef TOML_NORETURN
}
namespace toml

View File

@ -1,5 +1,5 @@
#pragma once
#include "toml_common.h"
#include "toml_date_time.h"
namespace toml::impl
{
@ -213,7 +213,7 @@ namespace toml::impl
}
template <typename CHAR>
inline void print_to_stream(const toml::time_offset& val, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
inline void print_to_stream(toml::time_offset val, std::basic_ostream<CHAR>& stream) TOML_MAY_THROW
{
static_assert(sizeof(CHAR) == 1);
if (!val.minutes)
@ -253,34 +253,3 @@ namespace toml::impl
print_to_stream(*val.time_offset, stream);
}
}
namespace toml
{
template <typename CHAR>
inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const date& rhs) TOML_MAY_THROW
{
impl::print_to_stream(rhs, lhs);
return lhs;
}
template <typename CHAR>
inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const time& rhs) TOML_MAY_THROW
{
impl::print_to_stream(rhs, lhs);
return lhs;
}
template <typename CHAR>
inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const time_offset& rhs) TOML_MAY_THROW
{
impl::print_to_stream(rhs, lhs);
return lhs;
}
template <typename CHAR>
inline std::basic_ostream<CHAR>& operator << (std::basic_ostream<CHAR>& lhs, const date_time& rhs) TOML_MAY_THROW
{
impl::print_to_stream(rhs, lhs);
return lhs;
}
}

View File

@ -94,7 +94,7 @@ namespace toml
friend class impl::parser;
friend class node_view<table>;
string_map<std::unique_ptr<node>> values;
impl::string_map<std::unique_ptr<node>> values;
bool inline_ = false;
public:

View File

@ -71,7 +71,7 @@ namespace toml::impl
}
[[nodiscard]]
constexpr bool is_hex_digit(char32_t codepoint) noexcept
constexpr bool is_hexadecimal_digit(char32_t codepoint) noexcept
{
return (codepoint >= U'a' && codepoint <= U'f')
|| (codepoint >= U'A' && codepoint <= U'F')

View File

@ -8,7 +8,7 @@
namespace toml::impl
{
/// \brief Returns true if a codepoint belongs to any of these categories: Ll, Lm, Lo, Lt, Lu
//# Returns true if a codepoint belongs to any of these categories: Ll, Lm, Lo, Lt, Lu
[[nodiscard]]
constexpr bool is_unicode_letter(char32_t codepoint) noexcept
{
@ -465,7 +465,7 @@ namespace toml::impl
// chunk summary: 125582 codepoints from 607 ranges (spanning a search area of 194932)
}
/// \brief Returns true if a codepoint belongs to any of these categories: Nd, Nl
//# Returns true if a codepoint belongs to any of these categories: Nd, Nl
[[nodiscard]]
constexpr bool is_unicode_number(char32_t codepoint) noexcept
{
@ -578,7 +578,7 @@ namespace toml::impl
// chunk summary: 856 codepoints from 70 ranges (spanning a search area of 123642)
}
/// \brief Returns true if a codepoint belongs to any of these categories: Mn, Mc
//# Returns true if a codepoint belongs to any of these categories: Mn, Mc
[[nodiscard]]
constexpr bool is_unicode_combining_mark(char32_t codepoint) noexcept
{

View File

@ -1,6 +1,5 @@
#pragma once
#include "toml_node.h"
#include "toml_print_to_stream.h"
namespace toml
{
@ -16,22 +15,12 @@ namespace toml
private:
friend class impl::parser;
template <typename U>
template <typename U, typename V>
[[nodiscard]] TOML_ALWAYS_INLINE
toml::value<U>* as_value() noexcept
static auto as_value([[maybe_unused]] V* ptr) noexcept
{
if constexpr (std::is_same_v<T, U>)
return this;
else
return nullptr;
}
template <typename U>
[[nodiscard]] TOML_ALWAYS_INLINE
const toml::value<U>* as_value() const noexcept
{
if constexpr (std::is_same_v<T, U>)
return this;
return ptr;
else
return nullptr;
}
@ -82,21 +71,21 @@ namespace toml
[[nodiscard]] bool is_time() const noexcept override { return std::is_same_v<T, time>; }
[[nodiscard]] bool is_date_time() const noexcept override { return std::is_same_v<T, date_time>; }
[[nodiscard]] value<string>* as_string() noexcept override { return as_value<string>(); }
[[nodiscard]] value<int64_t>* as_integer() noexcept override { return as_value<int64_t>(); }
[[nodiscard]] value<double>* as_floating_point() noexcept override { return as_value<double>(); }
[[nodiscard]] value<bool>* as_boolean() noexcept override { return as_value<bool>(); }
[[nodiscard]] value<date>* as_date() noexcept override { return as_value<date>(); }
[[nodiscard]] value<time>* as_time() noexcept override { return as_value<time>(); }
[[nodiscard]] value<date_time>* as_date_time() noexcept override { return as_value<date_time>(); }
[[nodiscard]] value<string>* as_string() noexcept override { return as_value<string>(this); }
[[nodiscard]] value<int64_t>* as_integer() noexcept override { return as_value<int64_t>(this); }
[[nodiscard]] value<double>* as_floating_point() noexcept override { return as_value<double>(this); }
[[nodiscard]] value<bool>* as_boolean() noexcept override { return as_value<bool>(this); }
[[nodiscard]] value<date>* as_date() noexcept override { return as_value<date>(this); }
[[nodiscard]] value<time>* as_time() noexcept override { return as_value<time>(this); }
[[nodiscard]] value<date_time>* as_date_time() noexcept override { return as_value<date_time>(this); }
[[nodiscard]] const value<string>* as_string() const noexcept override { return as_value<string>(); }
[[nodiscard]] const value<int64_t>* as_integer() const noexcept override { return as_value<int64_t>(); }
[[nodiscard]] const value<double>* as_floating_point() const noexcept override { return as_value<double>(); }
[[nodiscard]] const value<bool>* as_boolean() const noexcept override { return as_value<bool>(); }
[[nodiscard]] const value<date>* as_date() const noexcept override { return as_value<date>(); }
[[nodiscard]] const value<time>* as_time() const noexcept override { return as_value<time>(); }
[[nodiscard]] const value<date_time>* as_date_time() const noexcept override { return as_value<date_time>(); }
[[nodiscard]] const value<string>* as_string() const noexcept override { return as_value<string>(this); }
[[nodiscard]] const value<int64_t>* as_integer() const noexcept override { return as_value<int64_t>(this); }
[[nodiscard]] const value<double>* as_floating_point() const noexcept override { return as_value<double>(this); }
[[nodiscard]] const value<bool>* as_boolean() const noexcept override { return as_value<bool>(this); }
[[nodiscard]] const value<date>* as_date() const noexcept override { return as_value<date>(this); }
[[nodiscard]] const value<time>* as_time() const noexcept override { return as_value<time>(this); }
[[nodiscard]] const value<date_time>* as_date_time() const noexcept override { return as_value<date_time>(this); }
[[nodiscard]] T& get() & noexcept { return val_; }
[[nodiscard]] T&& get() && noexcept { return std::move(val_); }

View File

@ -2,7 +2,7 @@
import sys
import os
import os.path
import os.path as path
import traceback
import hashlib
import subprocess
@ -16,26 +16,30 @@ def is_tool(name):
def get_script_folder():
return os.path.dirname(os.path.realpath(sys.argv[0]))
return path.dirname(path.realpath(sys.argv[0]))
def read_all_text_from_file(path):
print("Reading {}".format(path))
file = open(path, 'r')
text = file.read()
file.close()
with open(path, 'r') as file:
text = file.read()
return text
def run_python_script(script_path, *args):
subprocess.check_call(
['py' if is_tool('py') else 'python3', script_path] + [arg for arg in args]
)
def main():
hpp_path = os.path.join(get_script_folder(), '..', 'toml.hpp')
hpp_path = path.join(get_script_folder(), '..', 'toml.hpp')
hash1 = hashlib.sha1(read_all_text_from_file(hpp_path).encode('utf-8')).hexdigest()
print("Hash 1: {}".format(hash1))
subprocess.check_call(
['py' if is_tool('py') else 'python3', 'generate_single_header.py']
)
run_python_script('generate_single_header.py')
hash2 = hashlib.sha1(read_all_text_from_file(hpp_path).encode('utf-8')).hexdigest()
print("Hash 2: {}".format(hash2))
if (hash1 != hash2):

View File

@ -0,0 +1,783 @@
#!/usr/bin/env python3
import sys
import re
import os
import os.path as path
import traceback
import datetime
import subprocess
import random
import concurrent.futures
import shutil
import html
import fnmatch
import bs4 as soup
_inlineNamespaces = [
"toml::literals",
]
_inlineNamespaceExplainer = 'All members of this namespace are automatically members of the parent namespace. ' \
+ 'It does not require an explicit \'using\' statement.'
def is_tool(name):
return shutil.which(name) is not None
def is_collection(val):
if isinstance(val, (list, tuple, dict, set)):
return True
return False
def get_all_files(dir, all=None, any=None):
files = [f for f in [path.join(dir, f) for f in os.listdir(dir)] if path.isfile(f)]
if (files and all is not None):
if (not is_collection(all)):
all = (all,)
all = [f for f in all if f is not None]
for fil in all:
files = fnmatch.filter(files, fil)
if (files and any is not None):
if (not is_collection(any)):
any = (any,)
any = [f for f in any if f is not None]
if any:
results = set()
for fil in any:
results.update(fnmatch.filter(files, fil))
files = [f for f in results]
return files
def print_value(name, val):
print('{}:\n {}'.format(name, val))
def error_message(err, prefix="Error"):
if (isinstance(err, Exception) or (is_collection(err) and isinstance(err[0], Exception))):
exc = err[0] if is_collection(err) else err
trace = err[1] if (is_collection(err) and len(err) > 1) else traceback.format_exc(err)
print('{}: [{}] {}\n{}'.format(
prefix,
type(err).__name__,
str(err),
traceback.format_exc(err)
),
file=sys.stderr
)
else:
print("{}: {}".format(prefix, err), file=sys.stderr)
class HTMLDocument(object):
def __init__(self, path):
self.__path = path
with open(path, 'r', encoding='utf-8') as f:
self.__doc = soup.BeautifulSoup(f, 'html5lib', from_encoding='utf-8')
self.head = self.__doc.head
self.body = self.__doc.body
def flush(self):
with open(self.__path, 'w', encoding='utf-8', newline='\n') as f:
f.write(str(self.__doc))
def new_tag(self, name, parent=None, string=None, class_=None, index=None, before=None, after=None, **kwargs):
tag = self.__doc.new_tag(name, **kwargs)
if (string is not None):
if (tag.string is not None):
tag.string.replace_with(string)
else:
tag.string = soup.NavigableString(string)
if (class_ is not None):
tag['class'] = class_
if (before is not None):
before.insert_before(tag)
elif (after is not None):
after.insert_after(tag)
elif (parent is not None):
if (index is None or index < 0):
parent.append(tag)
else:
parent.insert(index, tag)
return tag
def find_all_from_sections(self, name=None, select=None, section=None, **kwargs):
tags = []
sectionArgs = { }
if (section is not None):
sectionArgs['id'] = section
sections = self.body.main.article.div.div.div('section', recursive=False, **sectionArgs)
for sect in sections:
matches = sect(name, **kwargs) if name is not None else [ sect ]
if (select is not None):
newMatches = []
for match in matches:
newMatches += match.select(select)
matches = newMatches
tags += matches
return tags
def html_find_parent(tag,name,cutoff=None):
parent = tag.parent
while (parent is not None):
if (cutoff is not None and parent is cutoff):
return None
if (parent.name == name):
return parent;
parent = parent.parent
return parent
def html_replace_tag(tag,str):
doc = soup.BeautifulSoup(str, 'html5lib')
newTags = None
if (len(doc.body.contents) > 0):
newTags = [f for f in doc.body.contents]
newTags = [f.extract() for f in newTags]
prev = tag
for newTag in newTags:
prev.insert_after(newTag)
prev = newTag
else:
newTags = []
if (isinstance(tag, soup.NavigableString)):
tag.extract()
else:
tag.decompose()
return newTags
class RegexReplacer(object):
def __substitute(self, m):
self.__result = True
self.__groups = [str(m.group(0))]
self.__groups += [str(g) for g in m.groups()]
return self.__handler(m)
def __init__(self, expression, handler, value):
self.__handler = handler
self.__result = False
self.__value = expression.sub(lambda m: self.__substitute(m), value)
if (not self.__result):
self.__groups = []
def __str__(self):
return self.__value
def __bool__(self):
return self.__result
def __getitem__(self, key):
return self.__groups[key]
def __len__(self):
return len(self.__groups)
#=======================================================================================================================
# allows the injection of <div> and <span> tags using [div] and [span] proxies.
class CustomTagsFix(object):
__expression = re.compile(r"\[\s*(span|div)(.*?)\s*\](.*?)\[\s*/\s*\1\s*\]", re.I)
__allowedNames = ['dd', 'p']
@classmethod
def __substitute(cls, m):
return '<{}{}>{}</{}>'.format(
m.group(1),
html.unescape(m.group(2)),
m.group(3),
m.group(1)
)
def __call__(self, file, doc):
changed = False
for name in self.__allowedNames:
tags = doc.find_all_from_sections(name)
for tag in tags:
if (len(tag.contents) == 0 or html_find_parent(tag, 'a', doc.body) is not None):
continue
replacer = RegexReplacer(self.__expression, self.__substitute, str(tag))
if (replacer):
changed = True
html_replace_tag(tag, str(replacer))
return changed
#=======================================================================================================================
# adds custom links to the navbar.
class NavBarFix(object):
__links = [
('Report an issue', 'https://github.com/marzer/tomlplusplus/issues'),
('Github', 'https://github.com/marzer/tomlplusplus/')
]
def __call__(self, file, doc):
list = doc.body.header.nav.div.div.select_one('#m-navbar-collapse').div.ol
if (list.select_one('.tpp-injected') is None):
for label, url in self.__links:
doc.new_tag('a',
parent=doc.new_tag('li', parent=list, class_='tpp-injected tpp-external-navbar', index=0),
string=label,
href=url,
target='_blank'
)
return True
return False
#=======================================================================================================================
# changes any links to index.html to link to namespaces.html instead (index.html is blank/unused)
class IndexHrefFix(object):
def __call__(self, file, doc):
links = doc.body('a', href='index.html')
if (len(links) > 0):
for link in links:
link['href'] = 'namespaces.html'
return True
return False
#=======================================================================================================================
# base type for modifier parsing fixers.
class ModifiersFixBase(object):
_modifierRegex = "defaulted|noexcept|constexpr|(?:pure )?virtual|protected|__(?:(?:vector|std|fast)call|cdecl)"
_modifierClasses = {
"defaulted" : "m-info",
"noexcept" : "m-success",
"constexpr" : "m-primary",
"pure virtual" : "m-warning",
"virtual" : "m-warning",
"protected" : "m-warning",
"__vectorcall" : "m-info",
"__stdcall" : "m-info",
"__fastcall" : "m-info",
"__cdecl" : "m-info"
}
#=======================================================================================================================
# fixes improperly-parsed modifiers on function signatures in the various 'detail view' sections.
class ModifiersFix1(ModifiersFixBase):
__expression = re.compile(r'(\s+)({})(\s+)'.format(ModifiersFixBase._modifierRegex))
__sections = ['pub-static-methods', 'pub-methods', 'friends', 'func-members']
@classmethod
def __substitute(cls, m):
return '{}<span class="tpp-injected m-label m-flat {}">{}</span>{}'.format(
m.group(1),
cls._modifierClasses[m.group(2)],
m.group(2),
m.group(3)
)
def __call__(self, file, doc):
changed = False
for sect in self.__sections:
tags = doc.find_all_from_sections('dt', select='span.m-doc-wrap', section=sect)
for tag in tags:
replacer = RegexReplacer(self.__expression, self.__substitute, str(tag))
if (replacer):
changed = True
html_replace_tag(tag, str(replacer))
return changed
#=======================================================================================================================
# fixes improperly-parsed modifiers on function signatures in the 'Function documentation' section.
class ModifiersFix2(ModifiersFixBase):
__expression = re.compile(r'\s+({})\s+'.format(ModifiersFixBase._modifierRegex))
@classmethod
def __substitute(cls, m, matches):
matches.append(m.group(1))
return ' '
def __call__(self, file, doc):
changed = False
sections = doc.find_all_from_sections(section=False) # all sections without an id
section = None
for s in sections:
if (str(s.h2.string) == 'Function documentation'):
section = s
break
if (section is not None):
funcs = section(id=True)
funcs = [f.find('h3') for f in funcs]
for f in funcs:
bumper = f.select_one('span.m-doc-wrap-bumper')
end = f.select_one('span.m-doc-wrap').contents
end = end[len(end)-1]
matches = []
bumperContent = self.__expression.sub(lambda m: self.__substitute(m, matches), str(bumper))
if (matches):
changed = True
bumper = html_replace_tag(bumper, bumperContent)
lastInserted = end.find('span')
for match in matches:
lastInserted = doc.new_tag('span',
parent=end,
string=match,
class_='tpp-injected m-label {}'.format(self._modifierClasses[match]),
before=lastInserted
)
lastInserted.insert_after(' ')
return changed
#=======================================================================================================================
# base type for applying inline namespace annotations.
class InlineNamespaceFixBase(object):
_namespaceFiles = ['namespace{}.html'.format(ns.lower().replace('::','_1_1')) for ns in _inlineNamespaces]
#=======================================================================================================================
# adds inline namespace annotations in class and namespace trees.
class InlineNamespaceFix1(InlineNamespaceFixBase):
__allowedFiles = ['annotated.html', 'namespaces.html']
def __call__(self, file, doc):
global _inlineNamespaceExplainer
changed = False
if (file in self.__allowedFiles):
anchors = []
for f in self._namespaceFiles:
anchors += doc.body.find_all("a", href=f)
for anchor in anchors:
next = anchor.next_sibling
while (next is not None and isinstance(next, soup.NavigableString)):
next = next.next_sibling
if (next is not None and next.get('class') is not None and 'tpp-injected' in next.get('class')):
continue
doc.new_tag('span',
after=anchor,
string='inline',
title=_inlineNamespaceExplainer,
class_='m-label m-info m-flat tpp-injected tpp-inline-namespace'
)
anchor.insert_after(' ')
changed = True
return changed
#=======================================================================================================================
# adds inline namespace annotations to the h1 element of inline namespace pages.
class InlineNamespaceFix2(InlineNamespaceFixBase):
def __call__(self, file, doc):
global _inlineNamespaceExplainer
changed = False
if (file in self._namespaceFiles):
h1 = doc.body.find('h1')
tag = h1.select_one('span.tpp-injected')
if (tag is None):
tag = doc.new_tag('span',
parent=h1,
string='inline',
title=_inlineNamespaceExplainer,
class_='m-label m-info tpp-injected tpp-inline-namespace'
)
tag.insert_before(' ')
changed = True
return changed
#=======================================================================================================================
# adds inline namespace annotations to sections with id=namespaces.
class InlineNamespaceFix3(InlineNamespaceFixBase):
def __call__(self, file, doc):
global _inlineNamespaceExplainer
anchors = doc.find_all_from_sections('a', section='namespaces')
changed = False
for anchor in anchors:
if (anchor.get('href') not in self._namespaceFiles):
continue
next = anchor.next_sibling
while (next is not None and isinstance(next, soup.NavigableString)):
next = next.next_sibling
if (next is not None and next.get('class') is not None and 'tpp-injected' in next.get('class')):
continue
doc.new_tag('span',
after=anchor,
string='inline',
title=_inlineNamespaceExplainer,
class_='m-label m-info m-flat tpp-injected tpp-inline-namespace'
)
anchor.insert_after(' ')
changed = True
return changed
#=======================================================================================================================
# adds a custom footer to the main index pages.
class FooterFix(object):
__replacement = '<div id="tpp-custom-footer tpp-injected">Documentation generated {} using ' \
+ '<a href="https://mcss.mosra.cz/">m.css</a></div>'.format(
datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
)
def __call__(self, file, doc):
footer = doc.body.find(id='tpp-custom-footer')
if (footer is None):
return False
html_replace_tag(footer, self.__replacement)
return True
#=======================================================================================================================
# adds links to external sources where appropriate
class ExtDocLinksFix(object):
__types = [
(r'(?:std::)?size_t', 'https://en.cppreference.com/w/cpp/types/size_t'),
(r'(?:std::)?u?int(?:8|16|32|64)_ts?', 'https://en.cppreference.com/w/cpp/types/integer'),
(r'std::pairs?', 'https://en.cppreference.com/w/cpp/utility/pair'),
(r'std::bytes?', 'https://en.cppreference.com/w/cpp/types/byte'),
(r'std::optionals?', 'https://en.cppreference.com/w/cpp/utility/optional'),
(r'std::tuples?', 'https://en.cppreference.com/w/cpp/utility/tuple'),
(r'std::integral_constants?', 'https://en.cppreference.com/w/cpp/types/integral_constant'),
(r'std::char_traits', 'https://en.cppreference.com/w/cpp/string/char_traits'),
(r'std::allocators?', 'https://en.cppreference.com/w/cpp/memory/allocator'),
(r'std::enable_if(?:_t)?', 'https://en.cppreference.com/w/cpp/types/enable_if'),
(r'std::conditional(?:_t)?', 'https://en.cppreference.com/w/cpp/types/conditional'),
(r'std::unordered_maps?', 'https://en.cppreference.com/w/cpp/container/unordered_map'),
(r'std::unordered_sets?', 'https://en.cppreference.com/w/cpp/container/unordered_set'),
(r'std::maps?', 'https://en.cppreference.com/w/cpp/container/map'),
(r'std::sets?', 'https://en.cppreference.com/w/cpp/container/set'),
(r'std::vectors?', 'https://en.cppreference.com/w/cpp/container/vector'),
(r'std::arrays?', 'https://en.cppreference.com/w/cpp/container/array'),
(r'std::chrono::durations?', 'https://en.cppreference.com/w/cpp/chrono/duration'),
(
r'std::atomic(?:_(?:'
+ r'bool|[su]?char(?:8_t|16_t|32_t)?|u?short'
+ r'|u?int(?:8_t|16_t|32_t|64_t)?|u?l?long'
+ r'))?',
'https://en.cppreference.com/w/cpp/atomic/atomic'
),
(r'std::unique_ptrs?', 'https://en.cppreference.com/w/cpp/memory/unique_ptr'),
(r'std::shared_ptrs?', 'https://en.cppreference.com/w/cpp/memory/shared_ptr'),
(r'(?:std::)?nullptr_t', 'https://en.cppreference.com/w/cpp/types/nullptr_t'),
(r'std::reverse_iterator', 'https://en.cppreference.com/w/cpp/iterator/reverse_iterator'),
(r'std::(?:basic_|w)?istreams?', 'https://en.cppreference.com/w/cpp/io/basic_istream'),
(r'std::(?:basic_|w)?ostreams?', 'https://en.cppreference.com/w/cpp/io/basic_ostream'),
(r'std::(?:basic_|w)?iostreams?', 'https://en.cppreference.com/w/cpp/io/basic_iostream'),
(r'std::(?:basic_|w)?ifstreams?', 'https://en.cppreference.com/w/cpp/io/basic_ifstream'),
(r'std::(?:basic_|w)?ofstreams?', 'https://en.cppreference.com/w/cpp/io/basic_ofstream'),
(r'std::(?:basic_|w)?fstreams?', 'https://en.cppreference.com/w/cpp/io/basic_fstream'),
(r'std::(?:basic_|w)?istringstreams?', 'https://en.cppreference.com/w/cpp/io/basic_istringstream'),
(r'std::(?:basic_|w)?ostringstreams?', 'https://en.cppreference.com/w/cpp/io/basic_ostringstream'),
(r'std::(?:basic_|w)?stringstreams?', 'https://en.cppreference.com/w/cpp/io/basic_stringstream'),
(r'std::(?:basic_|w|u8)?string_views?', 'https://en.cppreference.com/w/cpp/string/basic_string_view'),
(r'std::(?:basic_|w|u8)?strings?', 'https://en.cppreference.com/w/cpp/string/basic_string'),
(r'char(?:8|16|32)_ts?', 'https://en.cppreference.com/w/cpp/language/types'),
(r'std::is_(?:nothrow_)?convertible(?:_v)?', 'https://en.cppreference.com/w/cpp/types/is_convertible'),
(r'std::is_same(?:_v)?', 'https://en.cppreference.com/w/cpp/types/is_same'),
(r'std::is_base_of(?:_v)?', 'https://en.cppreference.com/w/cpp/types/is_base_of'),
(r'std::is_enum(?:_v)?', 'https://en.cppreference.com/w/cpp/types/is_enum'),
(r'std::is_floating_point(?:_v)?', 'https://en.cppreference.com/w/cpp/types/is_floating_point'),
(r'std::is_integral(?:_v)?', 'https://en.cppreference.com/w/cpp/types/is_integral'),
(r'std::is_pointer(?:_v)?', 'https://en.cppreference.com/w/cpp/types/is_pointer'),
(r'std::is_reference(?:_v)?', 'https://en.cppreference.com/w/cpp/types/is_reference'),
(r'std::is_signed(?:_v)?', 'https://en.cppreference.com/w/cpp/types/is_signed'),
(r'std::is_unsigned(?:_v)?', 'https://en.cppreference.com/w/cpp/types/is_unsigned'),
(r'std::is_void(?:_v)?', 'https://en.cppreference.com/w/cpp/types/is_void'),
(r'std::is_(?:nothrow_)?invocable(?:_r)?', 'https://en.cppreference.com/w/cpp/types/is_invocable'),
(r'std::add_[lr]value_reference(?:_t)?', 'https://en.cppreference.com/w/cpp/types/add_reference'),
(r'std::remove_reference(?:_t)?', 'https://en.cppreference.com/w/cpp/types/remove_reference'),
(r'std::remove_cv(?:_t)?', 'https://en.cppreference.com/w/cpp/types/remove_cv'),
(
r'(?:L?P)?(?:'
+ r'D?WORD(?:32|64|_PTR)?|HANDLE|HMODULE|BOOL(?:EAN)?'
+ r'|U?SHORT|U?LONG|U?INT(?:8|16|32|64)?'
+ r'|BYTE|VOID|C[WT]?STR'
+ r')',
'https://docs.microsoft.com/en-us/windows/desktop/winprog/windows-data-types'
),
(
r'(?:__INTELLISENSE__|_MSC_FULL_VER|_MSC_VER|_MSVC_LANG|_WIN32|_WIN64)',
'https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2019'
)
]
__allowedNames = ['dd', 'p', 'dt', 'h3', 'td']
def __init__(self):
self.__expressions = []
for type, uri in self.__types:
self.__expressions.append((re.compile(type+'(?!</a>)'), uri))
@classmethod
def __substitute(cls, m, uri):
return r'<a href="{}" class="m-doc tpp-injected tpp-external" target="_blank">{}</a>'.format(
uri,
m.group(0)
)
def __process_tag(self, tag):
for expr, uri in self.__expressions:
for descendant in tag.descendants:
if (not isinstance(descendant, soup.NavigableString) or html_find_parent(descendant, 'a', tag) is not None):
continue
replacer = RegexReplacer(expr, lambda m: self.__substitute(m, uri), html.escape(str(descendant), quote=False))
if (replacer):
html_replace_tag(descendant, str(replacer))
return True
return False
def __call__(self, file, doc):
changed = False
for name in self.__allowedNames:
for tag in doc.body.main.article.div.div(name):
if (len(tag.contents) == 0 or html_find_parent(tag, 'a', doc.body) is not None):
continue
while (self.__process_tag(tag)):
changed = True
continue
return changed
#=======================================================================================================================
# collapses std::enable_if in template headers to reduce verbosity.
class EnableIfFix(object):
__expression = re.compile(
# group 1: everything left of and including "std::enable_if<" (or it's aliases)
r'^(.+?(?:template&lt;.+&gt;\s*)?template\s*&lt;.+?(?:typename|class)\s*(?:=\s*)?(?:<a.+?>\s*)?(?:std::enable_if(?:_t)?)\s*(?:</a>\s*)?&lt;)\s*'
# group 2: the SFINAE parameters we actually want from inside the std::enable_if
+ r'(.+?)'
# group 3: the rest of the template declaration on the right
+ r'\s*(&gt;\s*&gt;.+?)$',
re.S
)
__spacingFix1 = re.compile(r'(_v|>::value)(&&|\|\|)')
@classmethod
def __substitute(cls, m):
return r'{}<span class="tpp-injected tpp-enable-if"><a href="#" onclick="ToggleEnableIf(this);return false;">...</a><span>{}</span></span>{}'.format(
m.group(1),
m.group(2),
m.group(3)
)
def __call__(self, file, doc):
changed = False
for template in doc.body('div', class_='m-doc-template'):
replacer = RegexReplacer(self.__expression, lambda m: self.__substitute(m), str(template))
if replacer:
injected = html_replace_tag(template, str(replacer))[0].select_one(".tpp-enable-if")
anchor = injected.a
content = injected.span
tweaks = []
for tag in content.descendants:
if (isinstance(tag, soup.NavigableString)):
val = str(tag)
replacer = RegexReplacer(self.__spacingFix1, lambda m: '{} {}'.format(m[1], m[2]), val)
if replacer:
tweaks.append((tag,str(replacer)))
for tag, sub in tweaks:
tag.replace_with(sub)
anchor['title'] = content.get_text().strip().replace('"', '&quot;')
changed = True
return changed
#=======================================================================================================================
_threadError = None
def postprocess_file(dir, file, fixes):
global _threadError
if (_threadError is not None):
return False
print("Post-processing {}".format(file))
doc = HTMLDocument(path.join(dir, file))
file = file.lower()
changed = False
for fix in fixes:
if (fix(file, doc)):
changed = True
if (changed):
doc.flush()
return changed
def delete_directory(dir_path):
if (path.exists(dir_path)):
print('Deleting {}'.format(dir_path))
shutil.rmtree(dir_path)
def get_script_folder():
return path.dirname(path.realpath(sys.argv[0]))
def run_python_script(script_path, *args):
subprocess.check_call(
['py' if is_tool('py') else 'python3', script_path] + [arg for arg in args]
)
def main():
global _threadError
num_threads = os.cpu_count() * 2
root_dir = path.join(get_script_folder(), '..')
docs_dir = path.join(root_dir, 'docs')
xml_dir = path.join(docs_dir, 'xml')
html_dir = path.join(docs_dir, 'html')
mcss_dir = path.join(root_dir, 'extern', 'm.css')
doxygen = path.join(mcss_dir, 'documentation', 'doxygen.py')
print_value('doc', docs_dir)
print_value('xml', xml_dir)
print_value('html', html_dir)
print_value('m.css', mcss_dir)
print_value('doxygen', doxygen)
# delete any previously generated html and xml
delete_directory(xml_dir)
delete_directory(html_dir)
# run doxygen (via m.css)
run_python_script(doxygen, path.join(docs_dir, 'Doxyfile-mcss'))
# clean up xml and tmp files
delete_directory(xml_dir)
#for file in get_all_files(cwd, '*.tmp'):
# try:
# print('Deleting {}'.format(file))
# os.remove(file)
# except Exception as e:
# fatal_error(e)
# post-process html files
fixes = [
CustomTagsFix()
, NavBarFix()
, IndexHrefFix()
, ModifiersFix1()
, ModifiersFix2()
, InlineNamespaceFix1()
, InlineNamespaceFix2()
, InlineNamespaceFix3()
, FooterFix()
, ExtDocLinksFix()
, EnableIfFix()
]
files = [path.split(f) for f in get_all_files(html_dir, any=('*.html', '*.htm'))]
print_value("Files", files)
if files:
with concurrent.futures.ThreadPoolExecutor(max_workers=min(len(files), num_threads)) as executor:
jobs = { executor.submit(postprocess_file, dir, file, fixes) : file for dir, file in files }
for job in concurrent.futures.as_completed(jobs):
file = jobs[job]
try:
print('Finished processing {}.'.format(file))
except Exception as e:
_threadError = (e, traceback.format_exc(e))
executor.shutdown(False)
break
if (_threadError is not None):
error_message(_threadError, prefix="Fatal error")
sys.exit(-1)
if __name__ == '__main__':
try:
main()
except Exception as err:
print(
'Fatal error: [{}] {}'.format(
type(err).__name__,
str(err)
),
file=sys.stderr
)
traceback.print_exc(file=sys.stderr)
sys.exit(1)
sys.exit()

View File

@ -3,22 +3,21 @@
import sys
import re
import os
import os.path
import os.path as path
import traceback
def get_script_folder():
return os.path.dirname(os.path.realpath(sys.argv[0]))
return path.dirname(path.realpath(sys.argv[0]))
def read_all_text_from_file(path):
print("Reading {}".format(path))
file = open(path, 'r')
text = file.read()
file.close()
with open(path, 'r') as file:
text = file.read()
return text
@ -48,7 +47,7 @@ class Preprocessor:
return ''
self.processed_includes.append(incl)
text = read_all_text_from_file(os.path.join(get_script_folder(), '..', 'include', 'toml++', incl))
text = read_all_text_from_file(path.join(get_script_folder(), '..', 'include', 'toml++', incl))
text = re.sub(r'^\s*#\s*pragma\s+once\s*$', '', text, 0, re.I | re.M)
text = re.sub(r'^\s*//\s*clang-format\s+(?:off|on)\s*$', '', text, 0, re.I | re.M)
@ -58,7 +57,7 @@ class Preprocessor:
if (self.current_level == 1):
header_text = '' + raw_incl
lpad = 23 + ((25 * (self.header_indent % 4)) - int((len(header_text) + 4) / 2))
lpad = 28 + ((25 * (self.header_indent % 4)) - int((len(header_text) + 4) / 2))
self.header_indent += 1
return '\n{}\n#pragma region {}\n\n{}\n\n#pragma endregion {}\n{}'.format(
make_divider(header_text, lpad), '', text, '', make_divider('' + raw_incl, lpad))
@ -78,7 +77,8 @@ def main():
# preprocess header(s)
source_text = Preprocessor()('toml.h')
source_text = re.sub('\r\n', '\n', source_text, 0, re.I | re.M) # convert windows newlines
source_text = re.sub('^[ \t]*//[/#!].+?$', '', source_text, 0, re.I | re.M) # remove 'magic' comments
source_text = re.sub('(?:\n[ \t]*//[/#!<]+[^\n]*)+\n', '\n', source_text, 0, re.I | re.M) # remove 'magic' comment blocks
source_text = re.sub('^[ \t]*//[/#!<]+.+?$', '', source_text, 0, re.I | re.M) # remove 'magic' comments
source_text = re.sub('\n([ \t]*\n[ \t]*)+\n', '\n\n', source_text, 0, re.I | re.M) # remove double newlines
source_text = re.sub('([^ \t])[ \t]+\n', '\\1\n', source_text, 0, re.I | re.M) # remove trailing whitespace
source_text = source_text.strip()
@ -111,39 +111,38 @@ that contributed to this header can be found at the beginnings and ends of the c
TOML language specification:
Latest: https://github.com/toml-lang/toml/blob/master/README.md
v0.5.0: https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.5.0.md''')
preamble.append(read_all_text_from_file(os.path.join(get_script_folder(), '..', 'LICENSE')))
preamble.append(read_all_text_from_file(path.join(get_script_folder(), '..', 'LICENSE')))
# write the output file
output_file_path = os.path.join(get_script_folder(), '..', 'toml.hpp')
output_file_path = path.join(get_script_folder(), '..', 'toml.hpp')
print("Writing to {}".format(output_file_path))
output_file = open(output_file_path,'w', encoding='utf-8', newline='\n')
if (len(preamble) > 0):
print(make_divider(), file=output_file)
for pre in preamble:
print('//', file=output_file)
for line in pre.strip().splitlines():
print('//', file=output_file, end = '')
if (len(line) > 0):
print(' ', file=output_file, end = '')
print(line, file=output_file)
else:
print('\n', file=output_file, end = '')
print('//', file=output_file)
print(make_divider(), file=output_file)
print('''// clang-format off
with open(output_file_path,'w', encoding='utf-8', newline='\n') as output_file:
if (len(preamble) > 0):
print(make_divider(), file=output_file)
for pre in preamble:
print('//', file=output_file)
for line in pre.strip().splitlines():
print('//', file=output_file, end = '')
if (len(line) > 0):
print(' ', file=output_file, end = '')
print(line, file=output_file)
else:
print('\n', file=output_file, end = '')
print('//', file=output_file)
print(make_divider(), file=output_file)
print('''// clang-format off
#pragma once
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#endif
''', file=output_file)
print(source_text, file=output_file)
print('''
print(source_text, file=output_file)
print('''
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
// clang-format on''', file=output_file)
output_file.close()

View File

@ -3,7 +3,7 @@
import sys
import re
import os
import os.path
import os.path as path
import math
import requests
import traceback
@ -434,7 +434,7 @@ def emit_function(name, categories, file, codepoints):
# write the function
print('\n\t/// \\brief Returns true if a codepoint belongs to any of these categories: {}'.format(', '.join(categories)), file=file)
print('\n\t//# Returns true if a codepoint belongs to any of these categories: {}'.format(', '.join(categories)), file=file)
print('\t[[nodiscard]]', file=file)
print('\tconstexpr bool {}(char32_t codepoint) noexcept\n\t{{'.format(name), file=file)
root_chunk.print(file)
@ -447,7 +447,7 @@ def emit_function(name, categories, file, codepoints):
def get_script_folder():
return os.path.dirname(os.path.realpath(sys.argv[0]))
return path.dirname(path.realpath(sys.argv[0]))
@ -455,22 +455,21 @@ def main():
# get unicode character database
codepoint_list = ''
codepoint_file_path = os.path.join(get_script_folder(), 'UnicodeData.txt')
if (not os.path.exists(codepoint_file_path)):
codepoint_file_path = path.join(get_script_folder(), 'UnicodeData.txt')
if (not path.exists(codepoint_file_path)):
print("Couldn't find unicode database file, will download")
response = requests.get(
'https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt',
timeout=1
)
codepoint_list = response.text
codepoint_file = open(codepoint_file_path, 'w', newline='\n')
print(codepoint_list, end='', file=codepoint_file)
codepoint_file.close()
with open(codepoint_file_path, 'w', encoding='utf-8', newline='\n') as codepoint_file:
print(codepoint_list, end='', file=codepoint_file)
else:
print("Reading unicode database file into memory")
codepoint_file = open(codepoint_file_path, 'r')
codepoint_list = codepoint_file.read()
codepoint_file.close()
with open(codepoint_file_path, 'r') as codepoint_file:
codepoint_list = codepoint_file.read()
# parse the database file into codepoints
re_codepoint = re.compile(r'^([0-9a-fA-F]+);(.+?);([a-zA-Z]+);')
@ -498,19 +497,18 @@ def main():
codepoints.sort(key=lambda r:r[0])
# write the output file
output_file_path = os.path.join(get_script_folder(), '..', 'include', 'toml++', 'toml_utf8_generated.h')
output_file_path = path.join(get_script_folder(), '..', 'include', 'toml++', 'toml_utf8_generated.h')
print("Writing to {}".format(output_file_path))
output_file = open(output_file_path, 'w', newline='\n')
print('//# this file was generated by generate_unicode_functions.py', file=output_file)
print('#pragma once', file=output_file)
print('#include "toml_common.h"', file=output_file)
print('\n#define TOML_ASSUME_CODEPOINT_BETWEEN(first, last)\t\\\n\tTOML_ASSUME(codepoint >= first);\t\t\t\t\\\n\tTOML_ASSUME(codepoint <= last)', file=output_file)
print('\nnamespace toml::impl\n{', file=output_file, end='')
emit_function('is_unicode_letter', ('Ll', 'Lm', 'Lo', 'Lt', 'Lu'), output_file, codepoints)
emit_function('is_unicode_number', ('Nd', 'Nl'), output_file, codepoints)
emit_function('is_unicode_combining_mark', ('Mn', 'Mc'), output_file, codepoints)
print('}\n\n#undef TOML_ASSUME_CODEPOINT_BETWEEN', file=output_file)
output_file.close()
with open(output_file_path, 'w', encoding='utf-8', newline='\n') as output_file:
print('//# this file was generated by generate_unicode_functions.py', file=output_file)
print('#pragma once', file=output_file)
print('#include "toml_common.h"', file=output_file)
print('\n#define TOML_ASSUME_CODEPOINT_BETWEEN(first, last)\t\\\n\tTOML_ASSUME(codepoint >= first);\t\t\t\t\\\n\tTOML_ASSUME(codepoint <= last)', file=output_file)
print('\nnamespace toml::impl\n{', file=output_file, end='')
emit_function('is_unicode_letter', ('Ll', 'Lm', 'Lo', 'Lt', 'Lu'), output_file, codepoints)
emit_function('is_unicode_number', ('Nd', 'Nl'), output_file, codepoints)
emit_function('is_unicode_combining_mark', ('Mn', 'Mc'), output_file, codepoints)
print('}\n\n#undef TOML_ASSUME_CODEPOINT_BETWEEN', file=output_file)
if __name__ == '__main__':
try:

View File

@ -55,5 +55,5 @@
#if __has_include(<Catch2/single_include/catch2/catch.hpp>)
#include <Catch2/single_include/catch2/catch.hpp>
#else
#error Catch2 is missing! You probably need to fetch submodules ("git submodule update --init --recursive")
#error Catch2 is missing! You probably need to fetch submodules ("git submodule update --init --recursive extern/Catch2")
#endif

View File

@ -43,9 +43,9 @@ void parsing_should_succeed(std::basic_string_view<CHAR> toml_str, FUNC&& func,
catch (const toml::parse_error& err)
{
FAIL(
"Parse error on line "sv << err.where().begin.line
<< ", column "sv << err.where().begin.column
<< ":\n"sv << err.what()
"Parse error on line "sv << err.source().begin.line
<< ", column "sv << err.source().begin.column
<< ":\n"sv << err.description()
);
}
@ -58,9 +58,9 @@ void parsing_should_succeed(std::basic_string_view<CHAR> toml_str, FUNC&& func,
else
{
FAIL(
"Parse error on line "sv << result.error().where().begin.line
<< ", column "sv << result.error().where().begin.column
<< ":\n"sv << result.error().what()
"Parse error on line "sv << result.error().source().begin.line
<< ", column "sv << result.error().source().begin.column
<< ":\n"sv << result.error().description()
);
return;
}
@ -75,9 +75,9 @@ void parsing_should_succeed(std::basic_string_view<CHAR> toml_str, FUNC&& func,
else
{
FAIL(
"Parse error on line "sv << result.error().where().begin.line
<< ", column "sv << result.error().where().begin.column
<< ":\n"sv << result.error().what()
"Parse error on line "sv << result.error().source().begin.line
<< ", column "sv << result.error().source().begin.column
<< ":\n"sv << result.error().description()
);
return;
}
@ -160,7 +160,7 @@ void parse_expected_value(std::string_view value_str, const T& expected) noexcep
static constexpr auto is_val = [](char32_t codepoint) noexcept
{
if constexpr (std::is_same_v<string, promoted<T>>)
if constexpr (std::is_same_v<string, impl::promoted<T>>)
return codepoint == U'"' || codepoint == U'\'';
else
return !impl::is_whitespace(codepoint);
@ -201,8 +201,8 @@ void parse_expected_value(std::string_view value_str, const T& expected) noexcep
parsing_should_succeed(std::string_view{ value }, [&](table&& tbl) noexcept
{
CHECK(tbl.size() == 1);
REQUIRE(tbl[S("val"sv)].as<promoted<T>>());
CHECK(tbl[S("val"sv)].as<promoted<T>>()->get() == expected);
REQUIRE(tbl[S("val"sv)].as<impl::promoted<T>>());
CHECK(tbl[S("val"sv)].as<impl::promoted<T>>()->get() == expected);
CHECK(tbl[S("val"sv)].get()->source().begin == begin);
CHECK(tbl[S("val"sv)].get()->source().end == end);
});

2079
toml.hpp

File diff suppressed because it is too large Load Diff

View File

@ -59,6 +59,7 @@
<ClInclude Include="..\include\toml++\toml.h" />
<ClInclude Include="..\include\toml++\toml_array.h" />
<ClInclude Include="..\include\toml++\toml_common.h" />
<ClInclude Include="..\include\toml++\toml_date_time.h" />
<ClInclude Include="..\include\toml++\toml_default_formatter.h" />
<ClInclude Include="..\include\toml++\toml_formatter.h" />
<ClInclude Include="..\include\toml++\toml_json_formatter.h" />
@ -74,6 +75,8 @@
</ItemGroup>
<ItemGroup>
<None Include="..\.editorconfig" />
<None Include="..\python\ci_single_header_check.py" />
<None Include="..\python\generate_documentation.py" />
<None Include="..\python\generate_single_header.py" />
<None Include="..\python\generate_unicode_functions.py" />
<None Include="..\README.md" />

View File

@ -16,11 +16,14 @@
<ClInclude Include="..\include\toml++\toml_formatter.h" />
<ClInclude Include="..\include\toml++\toml_version.h" />
<ClInclude Include="..\include\toml++\toml_print_to_stream.h" />
<ClInclude Include="..\include\toml++\toml_date_time.h" />
</ItemGroup>
<ItemGroup>
<None Include="..\.editorconfig" />
<None Include="..\README.md" />
<None Include="..\python\generate_single_header.py" />
<None Include="..\python\generate_unicode_functions.py" />
<None Include="..\python\generate_documentation.py" />
<None Include="..\python\ci_single_header_check.py" />
</ItemGroup>
</Project>