fixed ML strings not allowing whitespace after line-ending backslashes

also:
- fixed value comparison with special floats
- added all the remaining conformance tests from BurntSushi/toml-test and iarna/toml-spec-tests
- added toml::inserter
- added license boilerplate to test files
This commit is contained in:
Mark Gillard 2020-06-26 21:01:27 +03:00
parent 7bf908fc11
commit b8438b3258
35 changed files with 3530 additions and 335 deletions

View File

@ -108,6 +108,7 @@ pre.m-code + pre.m-console
margin-top: -1.0rem;
border-top: 1px solid #444444;
font-size: 0.8rem;
background-color: #1a1c1d !important;
}
pre.m-code + pre.m-console span
{

View File

@ -191,6 +191,14 @@ namespace toml::impl
return new value{ std::forward<T>(val) };
}
}
template <typename T>
[[nodiscard]]
TOML_ALWAYS_INLINE
auto make_node(inserter<T>&& val) noexcept
{
return make_node(std::move(val.value));
}
}
namespace toml
@ -286,11 +294,30 @@ namespace toml
/// \out
/// [1, 2.0, "three", [4, 5]]
/// \eout
///
/// \tparam U One of the TOML node or value types (or a type promotable to one).
/// \tparam V One of the TOML node or value types (or a type promotable to one).
/// \param val The value used to initialize node 0.
///
/// \remark \parblock If you need to construct an array with one child array element, the array's move constructor
/// will take precedence and perform a move-construction instead. You can use toml::inserter to
/// suppress this behaviour: \cpp
/// // desired result: [ [ 42 ] ]
/// auto bad = toml::array{ toml::array{ 42 } }
/// auto good = toml::array{ toml::inserter{ toml::array{ 42 } } }
/// std::cout << "bad: " << bad << std::endl;
/// std::cout << "good:" << good << std::endl;
/// \ecpp
///
/// \out
/// bad: [ 42 ]
/// good: [ [ 42 ] ]
/// \eout
///
/// \endparblock
///
/// \tparam U One of the TOML node or value types (or a type promotable to one).
/// \tparam V One of the TOML node or value types (or a type promotable to one).
/// \param val The value used to initialize node 0.
/// \param vals The values used to initialize nodes 1...N.
///
/// \returns A TOML_NODISCARD_CTOR.
template <typename U, typename... V>
TOML_NODISCARD_CTOR
explicit array(U&& val, V&&... vals)

View File

@ -568,4 +568,27 @@ namespace toml
#if !TOML_ALL_INLINE
extern template TOML_API std::ostream& operator << (std::ostream&, node_type);
#endif
/// \brief Helper class for suppressing move-construction in single-argument array constructors.
///
/// \detail \cpp
/// // desired result: [ [ 42 ] ]
/// auto bad = toml::array{ toml::array{ 42 } }
/// auto good = toml::array{ toml::inserter{ toml::array{ 42 } } }
/// std::cout << "bad: " << bad << std::endl;
/// std::cout << "good:" << good << std::endl;
/// \ecpp
///
/// \out
/// bad: [ 42 ]
/// good: [ [ 42 ] ]
/// \eout
///
/// \see toml::array
template <typename T>
struct TOML_TRIVIAL_ABI inserter
{
T&& value;
};
template <typename T> inserter(T&&) -> inserter<T>;
}

View File

@ -549,9 +549,14 @@ namespace toml::impl
// handle 'line ending slashes' in multi-line mode
if constexpr (MultiLine)
{
if (is_line_break(*cp))
//consume_leading_whitespace
if (is_line_break(*cp) || is_whitespace(*cp))
{
consume_line_break();
consume_leading_whitespace();
if (!consume_line_break())
set_error_and_return_default(
"line-ending backslashes must be the last non-whitespace character on the line"sv
);
skipping_whitespace = true;
return_if_error({});
continue;

View File

@ -21,7 +21,7 @@ namespace toml::impl
return cp >= U'0' && cp <= U'f' && (1ull << (static_cast<ui64>(cp) - 0x30ull)) & 0x7E0000007E03FFull;
}
#if TOML_LANG_UNRELEASED // toml/issues/687 (unicode bare keys)
#if TOML_LANG_UNRELEASED // toml/issues/687 (unicode bare keys)
//# Returns true if a codepoint belongs to any of these categories:
//# Ll, Lm, Lo, Lt, Lu
@ -752,5 +752,5 @@ namespace toml::impl
//# chunk summary: 2282 codepoints from 293 ranges (spanning a search area of 917232)
}
#endif // TOML_LANG_UNRELEASED
#endif // TOML_LANG_UNRELEASED
} // toml::impl

View File

@ -193,7 +193,26 @@ namespace toml
}
/// \brief Value equality operator.
[[nodiscard]] friend bool operator == (const value& lhs, value_arg rhs) noexcept { return lhs.val_ == rhs; }
[[nodiscard]] friend bool operator == (const value& lhs, value_arg rhs) noexcept
{
if constexpr (std::is_same_v<value_type, double>)
{
static constexpr auto pack = [](auto l, auto r) constexpr noexcept
{
return ((static_cast<uint64_t>(l) << 32) | static_cast<uint64_t>(r));
};
switch (pack(std::fpclassify(lhs.val_), std::fpclassify(rhs)))
{
case pack(FP_INFINITE, FP_INFINITE): return (lhs.val_ < 0.0) == (rhs < 0.0);
case pack(FP_NAN, FP_NAN): return true;
default: return lhs.val_ == rhs;
}
}
else
return lhs.val_ == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const value&, value_arg, )
/// \brief Value less-than operator.
@ -224,7 +243,7 @@ namespace toml
[[nodiscard]] friend bool operator == (const value& lhs, const value<U>& rhs) noexcept
{
if constexpr (std::is_same_v<T, U>)
return lhs.val_ == rhs.val_;
return lhs == rhs.val_; //calls asymmetrical value-equality operator defined above
else
return false;
}

View File

@ -9,116 +9,430 @@ import os.path as path
import utils
import io
import re
def open_test_file(name):
test_file_path = path.join(utils.get_script_folder(), '..', 'tests', name)
print("Writing to {}".format(test_file_path))
return open(test_file_path, 'w', encoding='utf-8', newline='\n')
def emit_preamble(test_file):
write = lambda txt: print(txt, file=test_file)
write('#include "tests.h"')
write('using namespace toml::impl;')
write('')
write('#if !TOML_UNRELEASED_FEATURES // todo: improve conformance script to remove this')
write('')
import json
import yaml # pip install pyyaml
import math
import dateutil.parser # pip install python-dateutil
from datetime import datetime, date, time
def sanitize(s):
return re.sub(r'[ -]+', '_', s, 0, re.I | re.M)
return re.sub(r'[ _:;\/-]+', '_', s, 0, re.I | re.M)
def emit_invalid_tests(test_file, name, source_folder, skip_list=None):
constants_buf = io.StringIO('', newline='\n')
constants = lambda txt: print(txt, file=constants_buf)
tests_buf = io.StringIO('', newline='\n')
tests = lambda txt: print(txt, file=tests_buf)
def python_value_to_tomlpp(val):
if isinstance(val, str):
if re.fullmatch(r'^[+-]?[0-9]+[eE][+-]?[0-9]+$', val, re.M):
return str(float(val))
else:
return 'S(R"({})"sv)'.format(val)
elif isinstance(val, bool):
return 'true' if val else 'false'
elif isinstance(val, float):
if math.isinf(val):
return ('-' if val < 0.0 else '') + 'std::numeric_limits<double>::infinity()'
elif math.isnan(val):
return 'std::numeric_limits<double>::quiet_NaN()'
else:
return str(val)
elif isinstance(val, int):
if val == 9223372036854775807:
return 'INT64_MAX'
elif val == -9223372036854775808:
return 'INT64_MIN'
else:
return str(val)
elif isinstance(val, (TomlPPArray, TomlPPTable)):
return str(val)
elif isinstance(val, datetime):
offset = None
if val.tzinfo is not None:
offset = val.tzinfo.utcoffset(val)
mins = offset.total_seconds() / 60
offset = (int(mins / 60), int(mins % 60))
return 'toml::date_time{{ {{ {}, {}, {} }}, {{ {}, {}, {}, {}u }}{} }}'.format(
val.year,
val.month,
val.day,
val.hour,
val.minute,
val.second,
val.microsecond*1000,
'' if offset is None else ', {{ {}, {} }}'.format(offset[0], offset[1])
)
elif isinstance(val, date):
return 'toml::date{{ {}, {}, {} }}'.format(
val.year,
val.month,
val.day
)
elif isinstance(val, time):
return 'toml::time{{ {}, {}, {}, {} }}'.format(
val.hour,
val.minute,
val.second,
val.microsecond*1000
)
else:
raise ValueError(str(type(val)))
constants('namespace // invalid test data for {}'.format(name))
constants('{')
tests('TEST_CASE("conformance - invalid inputs from {}")'.format(name))
tests('{')
#files = [path.splitext(path.split(f)[1])[0] for f in utils.get_all_files(source_folder,all="*.toml")]
files = [path.split(f) for f in utils.get_all_files(source_folder, all="*.toml")]
files = [(f[0], *path.splitext(f[1])) for f in files]
for dir,file,ext in files:
if skip_list and file in skip_list:
class TomlPPArray:
def __init__(self, init_data=None):
self.values = init_data if init_data else list()
def render(self, indent = '', indent_declaration = False):
s = ''
if indent_declaration:
s += indent
if len(self.values) == 0:
s += 'toml::array{}'
else:
s += 'toml::array{'
for val in self.values:
s += '\n' + indent + '\t'
if isinstance(val, TomlPPArray) and len(self.values) == 1:
s += 'toml::inserter{'
if isinstance(val, (TomlPPTable, TomlPPArray)) and len(val) > 0:
s += val.render(indent + '\t')
else:
s += python_value_to_tomlpp(val)
if isinstance(val, TomlPPArray) and len(self.values) == 1:
s += '}'
s += ','
s += '\n' + indent + '}'
return s
def __str__(self):
return self.render()
def __len__(self):
return len(self.values)
class TomlPPTable:
def __init__(self, init_data=None):
self.values = init_data if init_data else dict()
def render(self, indent = '', indent_declaration = False):
s = ''
if indent_declaration:
s += indent
if len(self.values) == 0:
s += 'toml::table{}'
else:
s += 'toml::table{{'
for key, val in self.values.items():
s += '\n' + indent + '\t{ '
if isinstance(val, (TomlPPTable, TomlPPArray)) and len(val) > 0:
s += '\n' + indent + '\t\t{},'.format(python_value_to_tomlpp(str(key)))
#s += '\n' + val.render(indent + '\t\t')
s += ' ' + val.render(indent + '\t\t')
s += '\n' + indent + '\t'
else:
s += '{}, {} '.format(python_value_to_tomlpp(str(key)), python_value_to_tomlpp(val))
s += '},'
#else:
#s += '}\n'
s += '\n' + indent + '}}'
return s
def __str__(self):
return self.render()
def __len__(self):
return len(self.values)
def json_to_python(val):
if isinstance(val, dict):
if len(val) == 2 and "type" in val and "value" in val:
val_type = val["type"]
if val_type == "integer":
return int(val["value"])
elif val_type == "float":
return float(val["value"])
elif val_type == "string":
return str(val["value"])
elif val_type == "bool":
return True if val["value"].lower() == "true" else False
elif val_type == "array":
return json_to_python(val["value"])
elif val_type in ("datetime", "date", "time", "datetime-local"):
dt_val = dateutil.parser.parse(val["value"])
if val_type == "date":
return dt_val.date()
elif val_type == "time":
return dt_val.time()
else:
return dt_val
else:
raise ValueError(val_type)
else:
vals = dict()
for k,v in val.items():
vals[k] = json_to_python(v)
return vals
elif isinstance(val, list):
vals = list()
for v in val:
vals.append(json_to_python(v))
return vals
else:
raise ValueError(str(type(val)))
def python_to_tomlpp(node):
if isinstance(node, dict):
table = TomlPPTable()
for key, val in node.items():
table.values[key] = python_to_tomlpp(val)
return table
elif isinstance(node, (set, list, tuple)):
array = TomlPPArray()
for val in node:
array.values.append(python_to_tomlpp(val))
return array
else:
return node
class TomlTest:
def __init__(self, file_path, group, name, is_valid_case):
self.__name = name
self.__group = group
self.__identifier = sanitize(group + '_' + name)
self.__data = utils.read_all_text_from_file(file_path).strip()
self.condition = ''
if is_valid_case:
self.__expected = True
path_base = path.splitext(file_path)[0]
yaml_file = path_base + '.yaml'
if path.exists(yaml_file):
self.__expected = python_to_tomlpp(yaml.load(
utils.read_all_text_from_file(yaml_file),
Loader=yaml.FullLoader
))
else:
json_file = path_base + '.json'
if path.exists(json_file):
self.__expected = python_to_tomlpp(json_to_python(json.loads(
utils.read_all_text_from_file(json_file),
)))
else:
self.__expected = False
def name(self):
return self.__name
def group(self):
return self.__group
def identifier(self):
return self.__identifier
def data(self):
return self.__data
def expected(self):
return self.__expected
def __str__(self):
return 'static constexpr auto {} = S(R"({})"sv);'.format(
self.__identifier,
self.__data,
)
def load_tests(source_folder, group, is_valid_set, ignore_list):
tests = []
files = [(fp, path.splitext(path.split(fp)[1])[0]) for fp in utils.get_all_files(source_folder, all="*.toml")]
for file_path,name in files:
if ignore_list and name in ignore_list:
continue
identifier = sanitize(file)
constants('\t static constexpr auto {} = S(R"({})"sv);'.format(
identifier,
utils.read_all_text_from_file(path.join(dir, file + ext)).strip()
))
tests('\tparsing_should_fail(FILE_LINE_ARGS, {});'.format(identifier))
tests.append(TomlTest(file_path, group, name, is_valid_set))
return tests
constants('}')
tests('}')
write = lambda txt: print(txt, file=test_file)
write(constants_buf.getvalue())
write(tests_buf.getvalue())
def emit_appendix(test_file):
write = lambda txt: print(txt, file=test_file)
write('')
write('#endif // !TOML_UNRELEASED_FEATURES')
def set_condition(tests, condition, group, names):
for test in tests:
if test.group() == group and test.name() in names:
test.condition = condition
def main():
extern_root = path.join(utils.get_script_folder(), '..', 'extern')
with open_test_file('conformance.cpp') as test_file:
emit_preamble(test_file)
emit_invalid_tests(
test_file,
'BurntSushi/toml-test',
path.join(extern_root, 'toml-test', 'tests', 'invalid'),
(
# false negatives after TOML 0.4.0
'array-mixed-types-arrays-and-ints',
'array-mixed-types-ints-and-floats',
'array-mixed-types-strings-and-ints'
)
)
emit_invalid_tests(
test_file,
'iarna/toml-spec-tests',
path.join(extern_root, 'toml-spec-tests', 'errors'),
(
# I handle these internally, they get broken by I/O
'comment-control-1',
'comment-control-2',
'comment-control-3',
'comment-control-4',
'string-basic-control-1',
'string-basic-control-2',
'string-basic-control-3',
'string-basic-control-4',
'string-basic-multiline-control-1',
'string-basic-multiline-control-2',
'string-basic-multiline-control-3',
'string-basic-multiline-control-4',
'string-literal-control-1',
'string-literal-control-2',
'string-literal-control-3',
'string-literal-control-4',
'string-literal-multiline-control-1',
'string-literal-multiline-control-2',
'string-literal-multiline-control-3',
'string-literal-multiline-control-4',
)
)
emit_appendix(test_file)
tests = { 'valid': list(), 'invalid': list() }
tests['invalid'] += load_tests(path.join(extern_root, 'toml-test', 'tests', 'invalid'), 'burntsushi', False, (
# false negatives after TOML 0.4.0
'array-mixed-types-arrays-and-ints',
'array-mixed-types-ints-and-floats',
'array-mixed-types-strings-and-ints'
))
set_condition(tests['invalid'], '!TOML_LANG_UNRELEASED', 'burntsushi', (
'datetime-malformed-no-secs',
'inline-table-linebreak',
'multi-line-inline-table',
'string-byte-escapes'
))
tests['invalid'] += load_tests(path.join(extern_root, 'toml-spec-tests', 'errors'), 'iarna', False, (
# I test these explicitly in the other test files (they get broken by I/O)
'comment-control-1',
'comment-control-2',
'comment-control-3',
'comment-control-4',
'string-basic-control-1',
'string-basic-control-2',
'string-basic-control-3',
'string-basic-control-4',
'string-basic-multiline-control-1',
'string-basic-multiline-control-2',
'string-basic-multiline-control-3',
'string-basic-multiline-control-4',
'string-literal-control-1',
'string-literal-control-2',
'string-literal-control-3',
'string-literal-control-4',
'string-literal-multiline-control-1',
'string-literal-multiline-control-2',
'string-literal-multiline-control-3',
'string-literal-multiline-control-4'
))
set_condition(tests['invalid'], '!TOML_LANG_UNRELEASED', 'iarna', (
'inline-table-trailing-comma',
))
tests['valid'] += load_tests(path.join(extern_root, 'toml-test', 'tests', 'valid'), 'burntsushi', True, (
# newline/escape handling tests. these get broken by I/O (I test them separately)
'string-escapes',
# bugged: https://github.com/BurntSushi/toml-test/issues/58
'datetime'
))
tests['valid'] += load_tests(path.join(extern_root, 'toml-spec-tests', 'values'), 'iarna', True, (
# these are stress-tests for 'large' datasets. I test these separately. Having them inline in C++ code is insane.
'qa-array-inline-1000',
'qa-array-inline-nested-1000',
'qa-key-literal-40kb',
'qa-key-string-40kb',
'qa-scalar-literal-40kb',
'qa-scalar-literal-multiline-40kb',
'qa-scalar-string-40kb',
'qa-scalar-string-multiline-40kb',
'qa-table-inline-1000',
'qa-table-inline-nested-1000',
# newline/escape handling tests. these get broken by I/O (I test them separately)
'spec-newline-1',
'spec-newline-2',
'spec-newline-3',
'spec-string-escape-1',
'spec-string-escape-2',
'spec-string-escape-3',
'spec-string-escape-4',
'spec-string-escape-5',
'spec-string-escape-6',
'spec-string-escape-7',
'spec-string-escape-8',
'spec-string-escape-9',
# bugged: https://github.com/iarna/toml-spec-tests/issues/3
'spec-date-time-6',
'spec-date-time-local-2',
'spec-time-2',
# breaks gcc:
'spec-string-basic-multiline-4',
))
conditions = set()
for test_type, test_cases in tests.items():
for test in test_cases:
conditions.add(test.condition)
test_file_path = path.join(utils.get_script_folder(), '..', 'tests', 'conformance.cpp')
print("Writing to {}".format(test_file_path))
with open(test_file_path, 'w', encoding='utf-8', newline='\n') as test_file:
write = lambda txt: print(txt, file=test_file)
# preamble
write('// This file is a part of toml++ and is subject to the the terms of the MIT license.')
write('// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>')
write('// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.')
write('// SPDX-License-Identifier: MIT')
write('//-----')
write('// this file was generated by generate_conformance_tests.py - do not modify it directly')
write('')
write('#include "tests.h"')
write('using namespace toml::impl;')
write('')
# test data
write('TOML_PUSH_WARNINGS')
write('TOML_DISABLE_ALL_WARNINGS // unused variable spam')
write('')
write('namespace {')
write('')
for test_type, test_cases in tests.items():
write('namespace {}'.format(test_type))
write('{')
for test in test_cases:
write('\t{}'.format(test))
write('}')
write('')
write('}')
write('')
write('TOML_POP_WARNINGS')
write('')
# tests
write('TEST_CASE("conformance")')
write('{')
for test_type, test_cases in tests.items():
write('\t'+utils.make_divider(test_type + ' inputs', 20, line_length=116))
write('\t#if 1')
write('\t{')
for condition in conditions:
if condition != '':
write('')
write('\t\t#if {}'.format(condition));
for test in test_cases:
if test.condition != condition:
continue
expected = test.expected()
if isinstance(expected, bool):
if expected:
write('\t\tparsing_should_succeed(FILE_LINE_ARGS, {}::{});'.format(test_type, test.identifier()))
else:
write('\t\tparsing_should_fail(FILE_LINE_ARGS, {}::{});'.format(test_type, test.identifier()))
else:
write('')
write('\t\tparsing_should_succeed(FILE_LINE_ARGS, {}::{}, [](toml::table&& tbl)'.format(test_type, test.identifier()))
write('\t\t{')
write('\t\t\tauto expected = {};'.format(expected.render('\t\t\t')))
write('\t\t\tREQUIRE(tbl == expected);')
write('\t\t});')
if condition != '':
write('\t\t#endif // {}'.format(condition));
write('\t}')
write('\t#endif')
write('')
write('}')
write('')

View File

@ -74,7 +74,8 @@ type_names = [
'parse_error',
'json_formatter',
'default_formatter',
'format_flags'
'format_flags',
'inserter',
]
all_namespaces = [
'std',

View File

@ -11,18 +11,6 @@ import re
def make_divider(text = None, text_col = 40, pattern = '-'):
if (text is None):
return "//" + utils.repeat_pattern(pattern, 118)
else:
text = "//{} {} ".format(utils.repeat_pattern(pattern, text_col - 2), text);
if (len(text) < 120):
return text + utils.repeat_pattern(pattern, 120 - len(text))
else:
return text
class Preprocessor:
def __init__(self):
@ -48,7 +36,7 @@ class Preprocessor:
lpad = 28 + ((25 * (self.header_indent % 4)) - int((len(header_text) + 4) / 2))
self.header_indent += 1
text = '{}\n#if 1\n\n{}\n\n#endif\n{}\n'.format(
make_divider(header_text, lpad), text, make_divider('' + raw_incl, lpad)
utils.make_divider(header_text, lpad), text, utils.make_divider('' + raw_incl, lpad)
)
return '\n\n' + text + '\n\n' # will get merged later
@ -141,7 +129,7 @@ v0.5.0: https://toml.io/en/v0.5.0''')
print("Writing to {}".format(output_file_path))
with open(output_file_path,'w', encoding='utf-8', newline='\n') as output_file:
if (len(preamble) > 0):
print(make_divider(), file=output_file)
print(utils.make_divider(), file=output_file)
for pre in preamble:
print('//', file=output_file)
for line in pre.strip().splitlines():
@ -152,7 +140,7 @@ v0.5.0: https://toml.io/en/v0.5.0''')
else:
print('\n', file=output_file, end = '')
print('//', file=output_file)
print(make_divider(), file=output_file)
print(utils.make_divider(), file=output_file)
print('''// clang-format off
#ifndef INCLUDE_TOMLPLUSPLUS_H
#define INCLUDE_TOMLPLUSPLUS_H

View File

@ -1103,13 +1103,14 @@ def write_to_files(codepoints, header_file, test_file):
test = lambda txt: print(txt, file=test_file) if test_file is not None else None
both = lambda txt: (header(txt), test(txt))
header('//# This file is a part of toml++ and is subject to the the terms of the MIT license.')
header('//# Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>')
header('//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.')
header('// SPDX-License-Identifier: MIT')
header('//#-----')
header('//# this file was generated by generate_unicode_functions.py - do not modify it directly')
header('')
both('//# This file is a part of toml++ and is subject to the the terms of the MIT license.')
both('//# Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>')
both('//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.')
both('// SPDX-License-Identifier: MIT')
both('//#-----')
both('//# this file was generated by generate_unicode_functions.py - do not modify it directly')
both('')
header('#pragma once')
header('#include "toml_preprocessor.h"')
header('')

View File

@ -86,6 +86,18 @@ def get_all_files(dir, all=None, any=None):
def make_divider(text = None, text_col = 40, pattern = '-', line_length = 120):
if (text is None):
return "//" + repeat_pattern(pattern, line_length-2)
else:
text = "//{} {} ".format(repeat_pattern(pattern, text_col - 2), text);
if (len(text) < line_length):
return text + repeat_pattern(pattern, line_length - len(text))
else:
return text
def run(main_func):
try:
result = main_func()

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#pragma once
#include "settings.h"

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#define CATCH_CONFIG_RUNNER
#include "catch2.h"
#include <clocale>

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "settings.h"
#if !TOML_ALL_INLINE
#define TOML_IMPLEMENTATION

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
TEST_CASE("arrays - moving")

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
#if !TOML_EXCEPTIONS

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
TEST_CASE("tables - moving")

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
TEST_CASE("values - printing")

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
TEST_CASE("parsing - arrays")

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
TEST_CASE("parsing - booleans")

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
TEST_CASE("parsing - comments")

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
TOML_PUSH_WARNINGS

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
TOML_DISABLE_FLOAT_WARNINGS

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
TEST_CASE("parsing - integers (decimal)")

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
TEST_CASE("parsing - key-value pairs")

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
TOML_PUSH_WARNINGS

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
TEST_CASE("parsing - strings")

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
TEST_CASE("parsing - tables")

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#pragma once
// toml++ config

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
template bool parse_expected_value(std::string_view, uint32_t, std::string_view, const int&);

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#include "tests.h"
#include "unicode.h"
using namespace toml::impl;

View File

@ -1,3 +1,8 @@
// This file is a part of toml++ and is subject to the the terms of the MIT license.
// Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#pragma once
#include "tests.h"

View File

@ -1,3 +1,10 @@
//# This file is a part of toml++ and is subject to the the terms of the MIT license.
//# Copyright (c) 2019-2020 Mark Gillard <mark.gillard@outlook.com.au>
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
//#-----
//# this file was generated by generate_unicode_functions.py - do not modify it directly
#include "tests.h"
#include "unicode.h"
using namespace toml::impl;

View File

@ -889,6 +889,13 @@ namespace toml
#if !TOML_ALL_INLINE
extern template TOML_API std::ostream& operator << (std::ostream&, node_type);
#endif
template <typename T>
struct TOML_TRIVIAL_ABI inserter
{
T&& value;
};
template <typename T> inserter(T&&) -> inserter<T>;
}
#endif
@ -2098,7 +2105,26 @@ namespace toml
return *this;
}
[[nodiscard]] friend bool operator == (const value& lhs, value_arg rhs) noexcept { return lhs.val_ == rhs; }
[[nodiscard]] friend bool operator == (const value& lhs, value_arg rhs) noexcept
{
if constexpr (std::is_same_v<value_type, double>)
{
static constexpr auto pack = [](auto l, auto r) constexpr noexcept
{
return ((static_cast<uint64_t>(l) << 32) | static_cast<uint64_t>(r));
};
switch (pack(std::fpclassify(lhs.val_), std::fpclassify(rhs)))
{
case pack(FP_INFINITE, FP_INFINITE): return (lhs.val_ < 0.0) == (rhs < 0.0);
case pack(FP_NAN, FP_NAN): return true;
default: return lhs.val_ == rhs;
}
}
else
return lhs.val_ == rhs;
}
TOML_ASYMMETRICAL_EQUALITY_OPS(const value&, value_arg, )
[[nodiscard]] friend bool operator < (const value& lhs, value_arg rhs) noexcept { return lhs.val_ < rhs; }
[[nodiscard]] friend bool operator < (value_arg lhs, const value& rhs) noexcept { return lhs < rhs.val_; }
@ -2113,7 +2139,7 @@ namespace toml
[[nodiscard]] friend bool operator == (const value& lhs, const value<U>& rhs) noexcept
{
if constexpr (std::is_same_v<T, U>)
return lhs.val_ == rhs.val_;
return lhs == rhs.val_; //calls asymmetrical value-equality operator defined above
else
return false;
}
@ -2531,6 +2557,14 @@ namespace toml::impl
return new value{ std::forward<T>(val) };
}
}
template <typename T>
[[nodiscard]]
TOML_ALWAYS_INLINE
auto make_node(inserter<T>&& val) noexcept
{
return make_node(std::move(val.value));
}
}
namespace toml
@ -3448,7 +3482,7 @@ namespace toml::impl
return cp >= U'0' && cp <= U'f' && (1ull << (static_cast<ui64>(cp) - 0x30ull)) & 0x7E0000007E03FFull;
}
#if TOML_LANG_UNRELEASED // toml/issues/687 (unicode bare keys)
#if TOML_LANG_UNRELEASED // toml/issues/687 (unicode bare keys)
[[nodiscard]]
TOML_GNU_ATTR(const)
@ -4142,7 +4176,7 @@ namespace toml::impl
}
}
#endif // TOML_LANG_UNRELEASED
#endif // TOML_LANG_UNRELEASED
} // toml::impl
#endif
@ -7246,9 +7280,14 @@ namespace toml::impl
// handle 'line ending slashes' in multi-line mode
if constexpr (MultiLine)
{
if (is_line_break(*cp))
//consume_leading_whitespace
if (is_line_break(*cp) || is_whitespace(*cp))
{
consume_line_break();
consume_leading_whitespace();
if (!consume_line_break())
set_error_and_return_default(
"line-ending backslashes must be the last non-whitespace character on the line"sv
);
skipping_whitespace = true;
return_if_error({});
continue;