mirror of
https://github.com/marzer/tomlplusplus.git
synced 2024-09-15 15:13:21 +00:00
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:
parent
7bf908fc11
commit
b8438b3258
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
@ -287,10 +295,29 @@ namespace toml
|
||||
/// [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)
|
||||
|
@ -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>;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.append(TomlTest(file_path, group, name, is_valid_set))
|
||||
return tests
|
||||
|
||||
tests('\tparsing_should_fail(FILE_LINE_ARGS, {});'.format(identifier))
|
||||
|
||||
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('')
|
||||
|
||||
|
||||
|
||||
|
@ -74,7 +74,8 @@ type_names = [
|
||||
'parse_error',
|
||||
'json_formatter',
|
||||
'default_formatter',
|
||||
'format_flags'
|
||||
'format_flags',
|
||||
'inserter',
|
||||
]
|
||||
all_namespaces = [
|
||||
'std',
|
||||
|
@ -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
|
||||
|
@ -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('')
|
||||
|
@ -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()
|
||||
|
@ -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
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)")
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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&);
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
|
51
toml.hpp
51
toml.hpp
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user