Skip to content

Commit e1c120d

Browse files
committed
Introduce a format parsing context
This context will be used by the various formatters to parse replacement fields.
1 parent 2e52fa4 commit e1c120d

File tree

5 files changed

+281
-0
lines changed

5 files changed

+281
-0
lines changed

Makefile

+3
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@ $(eval $(call EXCLUDE_FROM_STYLE_ENFORCEMENT, extern/))
2121
# 3. Ignore external third-party files.
2222
# 4. Ignore literal_parser.hpp - this file is entirely constexpr functions that do not execute at
2323
# runtime, which llvm-cov doesn't seem to recognize.
24+
# 5. Ignore format_parameter_type.hpp - this file is entirely constexpr functions that do not
25+
# execute at runtime, which llvm-cov doesn't seem to recognize.
2426
$(eval $(call EXCLUDE_FROM_COVERAGE, test/))
2527
$(eval $(call EXCLUDE_FROM_COVERAGE, bench/))
2628
$(eval $(call EXCLUDE_FROM_COVERAGE, extern/))
2729
$(eval $(call EXCLUDE_FROM_COVERAGE, fly/types/numeric/detail/literal_parser.hpp))
30+
$(eval $(call EXCLUDE_FROM_COVERAGE, fly/types/string/detail/format_parameter_type.hpp))
2831

2932
# Paths to exclude from generation of compilation database.
3033
$(eval $(call EXCLUDE_FROM_COMPILATION_DATABASE, extern/))

build/win/libfly/libfly.vcxproj

+2
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,9 @@
231231
<ClInclude Include="..\..\..\fly\types\string\detail\classifier.hpp" />
232232
<ClInclude Include="..\..\..\fly\types\string\detail\converter.hpp" />
233233
<ClInclude Include="..\..\..\fly\types\string\detail\format_context.hpp" />
234+
<ClInclude Include="..\..\..\fly\types\string\detail\format_parameter_type.hpp" />
234235
<ClInclude Include="..\..\..\fly\types\string\detail\format_parameters.hpp" />
236+
<ClInclude Include="..\..\..\fly\types\string\detail\format_parse_context.hpp" />
235237
<ClInclude Include="..\..\..\fly\types\string\detail\format_specifier.hpp" />
236238
<ClInclude Include="..\..\..\fly\types\string\detail\format_string.hpp" />
237239
<ClInclude Include="..\..\..\fly\types\string\detail\stream_util.hpp" />

build/win/libfly/libfly.vcxproj.filters

+6
Original file line numberDiff line numberDiff line change
@@ -298,9 +298,15 @@
298298
<ClInclude Include="..\..\..\fly\types\string\detail\format_context.hpp">
299299
<Filter>types\string\detail</Filter>
300300
</ClInclude>
301+
<ClInclude Include="..\..\..\fly\types\string\detail\format_parameter_type.hpp">
302+
<Filter>types\string\detail</Filter>
303+
</ClInclude>
301304
<ClInclude Include="..\..\..\fly\types\string\detail\format_parameters.hpp">
302305
<Filter>types\string\detail</Filter>
303306
</ClInclude>
307+
<ClInclude Include="..\..\..\fly\types\string\detail\format_parse_context.hpp">
308+
<Filter>types\string\detail</Filter>
309+
</ClInclude>
304310
<ClInclude Include="..\..\..\fly\types\string\detail\format_specifier.hpp">
305311
<Filter>types\string\detail</Filter>
306312
</ClInclude>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#pragma once
2+
3+
#include "fly/types/string/detail/string_traits.hpp"
4+
5+
#include <cstdint>
6+
7+
namespace fly::detail {
8+
9+
/**
10+
* Enumerated list of supported format parameter types.
11+
*/
12+
enum class ParameterType : std::uint8_t
13+
{
14+
Generic,
15+
Character,
16+
String,
17+
Pointer,
18+
Integral,
19+
FloatingPoint,
20+
Boolean,
21+
};
22+
23+
/**
24+
* Map a format parameter type to the ParameterType enumeration.
25+
*
26+
* @tparam T The format parameter type.
27+
*
28+
* @return The mapped ParameterType enumeration.
29+
*/
30+
template <typename T>
31+
constexpr ParameterType infer_parameter_type()
32+
{
33+
using U = std::remove_cvref_t<T>;
34+
35+
if constexpr (is_supported_character_v<U>)
36+
{
37+
return ParameterType::Character;
38+
}
39+
else if constexpr (is_like_supported_string_v<U>)
40+
{
41+
return ParameterType::String;
42+
}
43+
else if constexpr (std::is_pointer_v<U> || std::is_null_pointer_v<U>)
44+
{
45+
return ParameterType::Pointer;
46+
}
47+
else if constexpr (
48+
BasicFormatTraits::is_integer_v<U> || BasicFormatTraits::is_default_formatted_enum_v<U>)
49+
{
50+
return ParameterType::Integral;
51+
}
52+
else if constexpr (std::is_floating_point_v<U>)
53+
{
54+
return ParameterType::FloatingPoint;
55+
}
56+
else if constexpr (std::is_same_v<U, bool>)
57+
{
58+
return ParameterType::Boolean;
59+
}
60+
else
61+
{
62+
return ParameterType::Generic;
63+
}
64+
}
65+
66+
} // namespace fly::detail
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
#pragma once
2+
3+
#include "fly/types/string/detail/format_parameter_type.hpp"
4+
#include "fly/types/string/detail/string_traits.hpp"
5+
#include "fly/types/string/lexer.hpp"
6+
7+
#include <cstddef>
8+
#include <string_view>
9+
10+
namespace fly::detail {
11+
12+
/**
13+
* Provides access to the format string parsing state consisting of the format string being parsed,
14+
* and the format parameter types and indices.
15+
*
16+
* @author Timothy Flynn ([email protected])
17+
* @version April 25, 2021
18+
*/
19+
template <typename CharType>
20+
class BasicFormatParseContext
21+
{
22+
using traits = BasicStringTraits<CharType>;
23+
using view_type = typename traits::view_type;
24+
25+
public:
26+
/**
27+
* Constructor.
28+
*
29+
* @param format The format string to be parsed.
30+
* @param parameters Pointer to a list of parameter types to be formatted.
31+
* @param parameters_size Size of the parameter types list.
32+
*/
33+
template <std::size_t N>
34+
constexpr explicit BasicFormatParseContext(
35+
const CharType (&format)[N],
36+
const ParameterType *parameters,
37+
std::size_t parameters_size) noexcept;
38+
39+
/**
40+
* Parse the optional position argument of the current replacement field. If a position was not
41+
* found, the position is observed to be the next format parameter in order.
42+
*
43+
* It is an error if the format string has a mix of manual and automatic positioning.
44+
*
45+
* @return The parsed or observed format parameter position.
46+
*/
47+
constexpr std::size_t next_position();
48+
49+
/**
50+
* Retrieve the type of the format parameter at the provided index.
51+
*
52+
* @param index The index of the format parameter.
53+
*
54+
* @return If the index exists, the format parameter type. Otherwise, an uninitialized value.
55+
*/
56+
constexpr std::optional<ParameterType> parameter_type(std::size_t position);
57+
58+
/**
59+
* @return The lexer for parsing the format string.
60+
*/
61+
constexpr fly::BasicLexer<CharType> &lexer();
62+
63+
/**
64+
* @return A string view into the format string.
65+
*/
66+
constexpr view_type view() const;
67+
68+
/**
69+
* Record an error that was encountered while parsing the format string.
70+
*
71+
* If invoked from a constant-evaluation context, this will raise a compilation error because
72+
* this method is purposefully non-constexpr. This results in an attempt to invoke a
73+
* non-constant expression from a constant context, which is erroneous. The error message from
74+
* the caller should be displayed in the terminal.
75+
*
76+
* If not invoked from a constant-evaluation context, this will store the error message.
77+
*
78+
* @param error A message describing the error that was encountered.
79+
*/
80+
void on_error(const char *error);
81+
82+
/**
83+
* If an error was stored from a non-constant-evaluated context, returns whether an error was
84+
* encountered while parsing the format string.
85+
*
86+
* @return True if an error was encountered while parsing.
87+
*/
88+
constexpr bool has_error() const;
89+
90+
/**
91+
* If an error was stored from a non-constant-evaluated context, returns the last error that was
92+
* encountered while parsing the format string.
93+
*
94+
* @return The error (if any) that was encountered while parsing the format string.
95+
*/
96+
std::string error() const;
97+
98+
private:
99+
BasicFormatParseContext(BasicFormatParseContext &&) = delete;
100+
BasicFormatParseContext &operator=(BasicFormatParseContext &&) = delete;
101+
102+
BasicFormatParseContext(const BasicFormatParseContext &) = delete;
103+
BasicFormatParseContext &operator=(const BasicFormatParseContext &) = delete;
104+
105+
fly::BasicLexer<CharType> m_lexer;
106+
107+
const ParameterType *m_parameters;
108+
std::size_t m_parameters_size;
109+
110+
std::size_t m_next_position {0};
111+
bool m_expect_no_positions_specified {false};
112+
bool m_expect_all_positions_specified {false};
113+
114+
std::string_view m_error;
115+
};
116+
117+
//==================================================================================================
118+
template <typename CharType>
119+
template <std::size_t N>
120+
constexpr BasicFormatParseContext<CharType>::BasicFormatParseContext(
121+
const CharType (&format)[N],
122+
const ParameterType *parameters,
123+
std::size_t parameters_size) noexcept :
124+
m_lexer(format),
125+
m_parameters(parameters),
126+
m_parameters_size(parameters_size)
127+
{
128+
}
129+
130+
//==================================================================================================
131+
template <typename CharType>
132+
constexpr std::size_t BasicFormatParseContext<CharType>::next_position()
133+
{
134+
std::size_t position = 0;
135+
136+
if (auto specified_position = m_lexer.consume_number(); specified_position)
137+
{
138+
m_expect_all_positions_specified = true;
139+
position = static_cast<std::size_t>(*specified_position);
140+
}
141+
else
142+
{
143+
m_expect_no_positions_specified = true;
144+
position = m_next_position++;
145+
}
146+
147+
if (m_expect_all_positions_specified && m_expect_no_positions_specified)
148+
{
149+
on_error("Argument position must be provided on all or not on any specifier");
150+
}
151+
152+
return position;
153+
}
154+
155+
//==================================================================================================
156+
template <typename CharType>
157+
constexpr std::optional<ParameterType>
158+
BasicFormatParseContext<CharType>::parameter_type(std::size_t position)
159+
{
160+
if (position >= m_parameters_size)
161+
{
162+
on_error("Argument position exceeds number of provided arguments");
163+
return std::nullopt;
164+
}
165+
166+
return m_parameters[position];
167+
}
168+
169+
//==================================================================================================
170+
template <typename CharType>
171+
constexpr inline fly::BasicLexer<CharType> &BasicFormatParseContext<CharType>::lexer()
172+
{
173+
return m_lexer;
174+
}
175+
176+
//==================================================================================================
177+
template <typename CharType>
178+
constexpr auto BasicFormatParseContext<CharType>::view() const -> view_type
179+
{
180+
return m_lexer.view();
181+
}
182+
183+
//==================================================================================================
184+
template <typename CharType>
185+
void BasicFormatParseContext<CharType>::on_error(const char *error)
186+
{
187+
m_error = error;
188+
}
189+
190+
//==================================================================================================
191+
template <typename CharType>
192+
constexpr bool BasicFormatParseContext<CharType>::has_error() const
193+
{
194+
return !m_error.empty();
195+
}
196+
197+
//==================================================================================================
198+
template <typename CharType>
199+
std::string BasicFormatParseContext<CharType>::error() const
200+
{
201+
return std::string(m_error.data(), m_error.size());
202+
}
203+
204+
} // namespace fly::detail

0 commit comments

Comments
 (0)