mimic++ v10.0.1
Loading...
Searching...
No Matches
Lexer.hpp
Go to the documentation of this file.
1// Copyright Dominic (DNKpp) Koepke 2024-2026.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at
4// https://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef MIMICPP_PRINTING_TYPE_LEXER_HPP
7#define MIMICPP_PRINTING_TYPE_LEXER_HPP
8
9#pragma once
10
11#include "mimic++/Fwd.hpp"
14
15#ifndef MIMICPP_DETAIL_IS_MODULE
16 #include <algorithm>
17 #include <array>
18 #include <cctype>
19 #include <functional>
20 #include <span>
21 #include <tuple>
22 #include <variant>
23#endif
24
26{
27 // This is a constexpr friendly version of `std::isspace`.
28 // see: https://en.cppreference.com/w/cpp/string/byte/isspace
29 inline auto constexpr is_space = [](char const c) noexcept {
30 switch (c)
31 {
32 case ' ': [[fallthrough]];
33 case '\f': [[fallthrough]];
34 case '\n': [[fallthrough]];
35 case '\r': [[fallthrough]];
36 case '\t': [[fallthrough]];
37 case '\v': return true;
38
39 default: return false;
40 }
41 };
42
43 // This is a constexpr friendly version of `std::isdigit`.
44 // see: https://en.cppreference.com/w/cpp/string/byte/isdigit
45 inline auto constexpr is_digit = [](char const c) noexcept {
46 return '0' <= c && c <= '9';
47 };
48
49 namespace texts
50 {
51 // just list the noteworthy ones here
52 inline std::array constexpr visibilityKeywords = std::to_array<StringViewT>({"public", "protected", "private"});
53 inline std::array constexpr specKeywords = std::to_array<StringViewT>({"const", "constexpr", "volatile", "noexcept", "static", "mutable"});
54 inline std::array constexpr contextKeywords = std::to_array<StringViewT>({"operator", "struct", "class", "enum"});
55 inline std::array constexpr typeKeywords = std::to_array<StringViewT>(
56 // The `__int64` keyword is used by msvc as an alias for `long long`.
57 {"auto", "void", "bool", "char", "char8_t", "char16_t", "char32_t", "wchar_t", "double", "float", "int", "long", "__int64", "short", "signed", "unsigned"});
58 inline std::array constexpr otherKeywords = std::to_array<StringViewT>({"new", "delete", "co_await"});
59 inline std::array constexpr digraphs = std::to_array<StringViewT>(
60 {"and", "or", "xor", "not", "bitand", "bitor", "compl", "and_eq", "or_eq", "xor_eq", "not_eq"});
61
62 inline std::array constexpr braceLikes = std::to_array<StringViewT>({"{", "}", "[", "]", "(", ")", "`", "'"});
63 inline std::array constexpr comparison = std::to_array<StringViewT>({"==", "!=", "<", "<=", ">", ">=", "<=>"});
64 inline std::array constexpr assignment = std::to_array<StringViewT>({"=", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>="});
65 inline std::array constexpr incOrDec = std::to_array<StringViewT>({"++", "--"});
66 inline std::array constexpr arithmetic = std::to_array<StringViewT>({"+", "-", "*", "/", "%"});
67 inline std::array constexpr bitArithmetic = std::to_array<StringViewT>({"~", "&", "|", "^", "<<", ">>"});
68 inline std::array constexpr logical = std::to_array<StringViewT>({"!", "&&", "||"});
69 inline std::array constexpr access = std::to_array<StringViewT>({".", ".*", "->", "->*"});
70 inline std::array constexpr rest = std::to_array<StringViewT>({"::", ";", ",", ":", "...", "?"});
71
72 // These operators exist, but are in fact alternative versions and will probably never encountered.
73 // inline std::array constexpr specialAngles = std::to_array<StringViewT>({"<:", ":>", "<%", "%>"});
74 }
75
76 [[nodiscard]]
77 consteval auto make_keyword_collection() noexcept
78 {
79 std::array collection = util::concat_arrays(
86
87 std::ranges::sort(collection);
88 MIMICPP_ASSERT(collection.cend() == std::ranges::unique(collection).begin(), "Fix your input!");
89
90 return collection;
91 }
92
93 [[nodiscard]]
95 {
96 // see: https://eel.is/c++draft/lex.operators#nt:operator-or-punctuator
97 std::array collection = util::concat_arrays(
107 std::ranges::sort(collection);
108 MIMICPP_ASSERT(collection.cend() == std::ranges::unique(collection).begin(), "Fix your input!");
109
110 return collection;
111 }
112
113 struct space
114 {
115 [[nodiscard]]
116 bool operator==(space const&) const = default;
117 };
118
119 struct keyword
120 {
121 public:
122 static constexpr std::array textCollection = make_keyword_collection();
123
124 [[nodiscard]]
125 explicit constexpr keyword(StringViewT const& text) noexcept
126 : keyword{
127 std::ranges::distance(
128 textCollection.cbegin(),
130 {
131 }
132
133 [[nodiscard]]
134 explicit constexpr keyword(std::ptrdiff_t const keywordIndex) noexcept
135 : m_KeywordIndex{keywordIndex}
136 {
137 MIMICPP_ASSERT(0 <= m_KeywordIndex && m_KeywordIndex < std::ranges::ssize(textCollection), "Invalid keyword.");
138 }
139
140 [[nodiscard]]
141 constexpr StringViewT text() const noexcept
142 {
143 return textCollection[m_KeywordIndex];
144 }
145
146 [[nodiscard]]
147 constexpr std::ptrdiff_t index() const noexcept
148 {
149 return m_KeywordIndex;
150 }
151
152 [[nodiscard]]
153 bool operator==(keyword const&) const = default;
154
155 private:
156 std::ptrdiff_t m_KeywordIndex;
157 };
158
160 {
161 public:
163
164 [[nodiscard]]
165 explicit constexpr operator_or_punctuator(StringViewT const& text) noexcept
167 std::ranges::distance(
168 textCollection.cbegin(),
170 {
171 }
172
173 [[nodiscard]]
174 explicit constexpr operator_or_punctuator(std::ptrdiff_t const textIndex) noexcept
175 : m_TextIndex{textIndex}
176 {
177 MIMICPP_ASSERT(0 <= m_TextIndex && m_TextIndex < std::ranges::ssize(textCollection), "Invalid operator or punctuator.");
178 }
179
180 [[nodiscard]]
181 constexpr StringViewT text() const noexcept
182 {
183 return textCollection[m_TextIndex];
184 }
185
186 [[nodiscard]]
187 constexpr std::ptrdiff_t index() const noexcept
188 {
189 return m_TextIndex;
190 }
191
192 [[nodiscard]]
193 bool operator==(operator_or_punctuator const&) const = default;
194
195 private:
196 std::ptrdiff_t m_TextIndex;
197 };
198
200 {
202
203 [[nodiscard]]
204 bool operator==(identifier const&) const = default;
205 };
206
207 struct literal
208 {
210
211 [[nodiscard]]
212 bool operator==(literal const&) const = default;
213 };
214
215 struct end
216 {
217 [[nodiscard]]
218 bool operator==(end const&) const = default;
219 };
220
221 using token_class = std::variant<
222 end,
223 space,
224 keyword,
227 literal>;
228
234
236 {
237 public:
238 [[nodiscard]]
239 explicit constexpr NameLexer(StringViewT text) noexcept
240 : m_Text{std::move(text)},
241 m_Next{find_next()}
242 {
243 }
244
245 [[nodiscard]]
246 constexpr token next() noexcept
247 {
248 return std::exchange(m_Next, find_next());
249 }
250
251 [[nodiscard]]
252 constexpr token const& peek() const noexcept
253 {
254 return m_Next;
255 }
256
257 private:
258 StringViewT m_Text;
259 token m_Next;
260
261 [[nodiscard]]
262 constexpr token find_next() noexcept
263 {
264 if (m_Text.empty())
265 {
266 return token{
267 .content = {m_Text.cend(), m_Text.cend()},
268 .classification = end{}
269 };
270 }
271
272 if (is_space(m_Text.front()))
273 {
274 // Multiple consecutive spaces or any whitespace character other than a single space
275 // carry no meaningful semantic value beyond delimitation.
276 // Although single spaces may sometimes influence the result and sometimes not,
277 // complicating the overall process, we filter out all non-single whitespace characters here.
278 if (StringViewT const content = next_as_space();
279 " " == content)
280 {
281 return token{
282 .content = content,
283 .classification = space{}};
284 }
285
286 return find_next();
287 }
288
289 if (auto const options = util::prefix_range(
291 m_Text.substr(0u, 1u)))
292 {
293 return next_as_op_or_punctuator(options);
294 }
295
296 if (std::optional literal = try_next_as_literal())
297 {
298 return token{
299 .content = literal->content,
300 .classification = *std::move(literal)};
301 }
302
303 StringViewT const content = next_as_identifier();
304 // As we do not perform any prefix-checks, we need to check now whether the token actually denotes a keyword.
305 if (auto const iter = util::binary_find(keyword::textCollection, content);
306 iter != keyword::textCollection.cend())
307 {
308 return token{
309 .content = content,
310 .classification = keyword{std::ranges::distance(keyword::textCollection.begin(), iter)}};
311 }
312
313 return token{
314 .content = content,
315 .classification = identifier{.content = content}};
316 }
317
318 [[nodiscard]]
319 constexpr StringViewT next_as_space() noexcept
320 {
321 auto const end = std::ranges::find_if_not(m_Text.cbegin() + 1, m_Text.cend(), is_space);
322 StringViewT const content{m_Text.cbegin(), end};
323 m_Text = StringViewT{end, m_Text.cend()};
324
325 return content;
326 }
327
332 [[nodiscard]]
333 constexpr token next_as_op_or_punctuator(std::span<StringViewT const> options) noexcept
334 {
335 MIMICPP_ASSERT(m_Text.substr(0u, 1u) == options.front(), "Assumption does not hold.");
336
337 auto const try_advance = [&, this](std::size_t const n) {
338 if (n <= m_Text.size())
339 {
340 return util::prefix_range(
341 options,
342 StringViewT{m_Text.cbegin(), m_Text.cbegin() + n});
343 }
344
345 return std::ranges::subrange{options.end(), options.end()};
346 };
347
348 std::size_t length{1u};
349 StringViewT const* lastMatch = &options.front();
350 while (auto const nextOptions = try_advance(length + 1))
351 {
352 ++length;
353 options = {nextOptions.begin(), nextOptions.end()};
354
355 // If the first string is exactly the size of the prefix, it's a match.
356 if (auto const& front = options.front();
357 length == front.size())
358 {
359 lastMatch = &front;
360 }
361 }
362
363 MIMICPP_ASSERT(!options.empty(), "Invalid state.");
364 MIMICPP_ASSERT(lastMatch, "Invalid state.");
365
366 auto const index = std::ranges::distance(operator_or_punctuator::textCollection.data(), lastMatch);
367 StringViewT const content{m_Text.substr(0u, lastMatch->size())};
368 m_Text.remove_prefix(lastMatch->size());
369
370 return token{
371 .content = content,
372 .classification = operator_or_punctuator{index}};
373 }
374
385 [[nodiscard]]
386 constexpr StringViewT next_as_identifier() noexcept
387 {
388 auto const last = std::ranges::find_if_not(
389 m_Text.cbegin() + 1,
390 m_Text.cend(),
391 [](auto const c) {
392 return !is_space(c)
393 && !std::ranges::binary_search(operator_or_punctuator::textCollection, StringViewT{&c, 1u});
394 });
395
396 StringViewT const content{m_Text.cbegin(), last};
397 m_Text = {last, m_Text.cend()};
398
399 return content;
400 }
401
402 // This function currently implements just a very basic integer-literal detection.
403 // see: https://eel.is/c++draft/lex.literal.kinds#nt:literal
404 [[nodiscard]]
405 constexpr std::optional<literal> try_next_as_literal() noexcept
406 {
407 MIMICPP_ASSERT(!m_Text.empty(), "Empty text.");
408
409 if (is_digit(m_Text.front()))
410 {
411 if ('0' == m_Text.front()
412 && 2u <= m_Text.size())
413 {
414 // Todo: one of
415 // https://eel.is/c++draft/lex.icon#nt:binary-literal
416 // https://eel.is/c++draft/lex.icon#nt:octal-literal
417 // https://eel.is/c++draft/lex.icon#nt:hexadecimal-prefix
418 }
419
420 // Todo: this is just a very naive approach.
421 auto const last = std::ranges::find_if_not(m_Text.cbegin() + 1, m_Text.cend(), is_digit);
422 StringViewT const content{m_Text.cbegin(), last};
423 m_Text = {last, m_Text.cend()};
424
425 return literal{.content = content};
426 }
427
428 return std::nullopt;
429 }
430 };
431}
432
433#endif
#define MIMICPP_ASSERT(condition, msg)
Definition Config.hpp:54
constexpr token next() noexcept
Definition Lexer.hpp:246
constexpr token const & peek() const noexcept
Definition Lexer.hpp:252
constexpr NameLexer(StringViewT text) noexcept
Definition Lexer.hpp:239
std::array constexpr incOrDec
Definition Lexer.hpp:65
std::array constexpr assignment
Definition Lexer.hpp:64
std::array constexpr contextKeywords
Definition Lexer.hpp:54
std::array constexpr typeKeywords
Definition Lexer.hpp:55
std::array constexpr rest
Definition Lexer.hpp:70
std::array constexpr logical
Definition Lexer.hpp:68
std::array constexpr otherKeywords
Definition Lexer.hpp:58
std::array constexpr digraphs
Definition Lexer.hpp:59
std::array constexpr arithmetic
Definition Lexer.hpp:66
std::array constexpr comparison
Definition Lexer.hpp:63
std::array constexpr braceLikes
Definition Lexer.hpp:62
std::array constexpr specKeywords
Definition Lexer.hpp:53
std::array constexpr visibilityKeywords
Definition Lexer.hpp:52
std::array constexpr access
Definition Lexer.hpp:69
std::array constexpr bitArithmetic
Definition Lexer.hpp:67
Definition Lexer.hpp:26
std::variant< end, space, keyword, operator_or_punctuator, identifier, literal > token_class
Definition Lexer.hpp:221
consteval auto make_operator_or_punctuator_collection() noexcept
Definition Lexer.hpp:94
auto constexpr is_space
Definition Lexer.hpp:29
auto constexpr is_digit
Definition Lexer.hpp:45
consteval auto make_keyword_collection() noexcept
Definition Lexer.hpp:77
constexpr std::ranges::borrowed_subrange_t< Range > prefix_range(Range &&range, Prefix &&prefix)
Returns a view containing all elements, which start with the given prefix.
Definition Algorithm.hpp:192
constexpr std::array< T, firstN+secondN > concat_arrays(std::array< T, firstN > const &first, std::array< T, secondN > const &second)
Concatenates the given arrays by copying all elements into a new array.
Definition Algorithm.hpp:218
constexpr detail::binary_find_fn binary_find
Finds the specified value within the container and returns an iterator pointing to it....
Definition Algorithm.hpp:361
std::basic_string_view< CharT, CharTraitsT > StringViewT
Definition Fwd.hpp:398
Definition Lexer.hpp:216
bool operator==(end const &) const =default
bool operator==(identifier const &) const =default
StringViewT content
Definition Lexer.hpp:201
constexpr keyword(StringViewT const &text) noexcept
Definition Lexer.hpp:125
constexpr keyword(std::ptrdiff_t const keywordIndex) noexcept
Definition Lexer.hpp:134
constexpr StringViewT text() const noexcept
Definition Lexer.hpp:141
constexpr std::ptrdiff_t index() const noexcept
Definition Lexer.hpp:147
bool operator==(keyword const &) const =default
static constexpr std::array textCollection
Definition Lexer.hpp:122
bool operator==(literal const &) const =default
StringViewT content
Definition Lexer.hpp:209
constexpr std::ptrdiff_t index() const noexcept
Definition Lexer.hpp:187
bool operator==(operator_or_punctuator const &) const =default
constexpr StringViewT text() const noexcept
Definition Lexer.hpp:181
static constexpr std::array textCollection
Definition Lexer.hpp:162
constexpr operator_or_punctuator(std::ptrdiff_t const textIndex) noexcept
Definition Lexer.hpp:174
constexpr operator_or_punctuator(StringViewT const &text) noexcept
Definition Lexer.hpp:165
bool operator==(space const &) const =default
token_class classification
Definition Lexer.hpp:232
StringViewT content
Definition Lexer.hpp:231