23 [[nodiscard]]
size_t find_line_number(std::string_view of_string, std::string_view in_document)
noexcept;
27 [[nodiscard]] std::pair<size_t, size_t>
find_line_and_column(std::string_view of_string, std::string_view in_document)
noexcept;
34#if __cpp_lib_to_chars || defined(DOXYGEN)
35 [[nodiscard]] std::pair<std::string_view, double> consume_c_float(std::string_view& str);
36 [[nodiscard]] std::pair<std::string_view, int64_t> consume_c_integer(std::string_view& str,
int base);
37 [[nodiscard]] std::pair<std::string_view, uint64_t> consume_c_unsigned(std::string_view& str,
int base );
39 template <
char DELIMITER>
40 [[nodiscard]] std::pair<std::string_view, std::string> consume_c_string(std::string_view& strv);
43 bool try_eat(std::string_view& str, std::string_view what);
44 bool try_eat(std::string_view& str,
char what);
45 void eat(std::string_view& str, std::string_view what);
46 void eat(std::string_view& str,
char what);
47 std::string_view try_eat_identifier(std::string_view& str);
48 std::string_view eat_identifier(std::string_view& str);
49 std::string_view try_eat_identifier_with(std::string_view& str, std::string_view additional_chars);
50 std::string_view eat_identifier_with(std::string_view& str, std::string_view additional_chars);
51 std::string_view eat_whitespace(std::string_view& str);
52 bool try_eat_line_comment(std::string_view& str, std::string_view comment_start);
53 bool try_eat_unsigned(std::string_view& str, uint64_t& result,
int base);
54 std::optional<uint64_t> try_eat_unsigned(std::string_view& str,
int base);
55 bool try_eat_integer(std::string_view& str, int64_t& result,
int base);
56 std::optional<int64_t> try_eat_integer(std::string_view& str,
int base);
57 bool try_eat_float(std::string_view& str,
double& result);
58 std::optional<double> try_eat_float(std::string_view& str);
59 uint64_t eat_unsigned(std::string_view& str,
int base);
60 int64_t eat_integer(std::string_view& str,
int base);
63 char32_t try_eat_utf8_codepoint(std::string_view& str);
64 char32_t eat_utf8_codepoint(std::string_view& str);
67 struct parse_error : std::runtime_error
71 template <GHPL_FORMAT_TEMPLATE>
72 parse_error(std::string_view where, GHPL_FORMAT_ARGS)
73 : runtime_error(GHPL_FORMAT_CALL)
88 [[nodiscard]]
inline size_t find_line_number(std::string_view of_string, std::string_view in_document)
noexcept
90 if (of_string.empty() || in_document.empty())
92 const auto start_line = of_string.data();
93 const auto start_doc = in_document.data();
94 if (start_line < start_doc || start_line > start_doc + in_document.size())
96 return std::count(start_doc, start_line,
'\n') + 1;
99 [[nodiscard]]
inline std::pair<size_t, size_t>
find_line_and_column(std::string_view of_string, std::string_view in_document)
noexcept
101 if (of_string.empty() || in_document.empty())
103 const auto start_line = of_string.data();
104 auto start_doc = in_document.data();
105 if (start_line < start_doc || start_line > start_doc + in_document.size())
109 auto last_line = start_doc;
111 for (; start_line != start_doc; ++start_doc) {
112 if (*start_doc ==
'\n') {
114 last_line = start_doc + 1;
118 return { count + 1, start_line - last_line + 1 };
123 if (str.empty() || !ascii::isidentstart(str[0]))
126 const auto start = str.begin();
127 str.remove_prefix(1);
128 trim_while(str, ascii::isident);
129 return make_sv(start, str.begin());
134 if (str.empty() || !(ascii::isidentstart(str[0]) ||
string_contains(additional_chars, str[0])))
137 const auto start = str.begin();
138 str.remove_prefix(1);
139 trim_while(str, [additional_chars](
char c) {
return ascii::isident(c) ||
string_contains(additional_chars, c); });
140 return make_sv(start, str.begin());
144#if __cpp_lib_to_chars || defined(DOXYGEN)
145 [[nodiscard]]
inline std::pair<std::string_view, double> consume_c_float(std::string_view& str)
147 if (str.empty() || !(ascii::isdigit(str[0]) || str[0] ==
'-'))
150 std::pair<std::string_view, double> result;
152 const auto from_chars_result =
from_chars(str, result.second);
153 if (from_chars_result.ec != std::errc{})
154 return { {}, std::numeric_limits<double>::quiet_NaN() };
156 result.first = make_sv(str.data(), from_chars_result.ptr);
157 str.remove_prefix(result.first.size());
161 [[nodiscard]]
inline std::pair<std::string_view, int64_t> consume_c_integer(std::string_view& str,
int base = 10)
163 if (str.empty() || !(ascii::isdigit(str[0]) || str[0] ==
'-'))
166 std::pair<std::string_view, int64_t> result;
168 const auto from_chars_result =
from_chars(str, result.second, base);
169 if (from_chars_result.ec != std::errc{})
172 result.first = make_sv(str.data(), from_chars_result.ptr);
173 str.remove_prefix(result.first.size());
177 [[nodiscard]]
inline std::pair<std::string_view, uint64_t> consume_c_unsigned(std::string_view& str,
int base = 10)
179 if (str.empty() || !ascii::isdigit(str[0]))
182 std::pair<std::string_view, uint64_t> result;
184 const auto from_chars_result = std::from_chars(str.data(), str.data() + str.size(), result.second, base);
185 if (from_chars_result.ec != std::errc{})
188 result.first = make_sv(str.data(), from_chars_result.ptr);
189 str.remove_prefix(result.first.size());
193 [[deprecated(
"WARNING: This function is incomplete")]]
194 [[nodiscard]]
inline std::tuple<std::string_view, std::variant<double, uint64_t, int64_t>> consume_c_number(std::string_view& str)
199 if (
auto first_char = str[0]; first_char ==
'-')
206 return consume_c_unsigned(str, 16);
208 return consume_c_unsigned(str, 1);
210 return consume_c_unsigned(str, 8);
211 else if (ascii::isdigit(first_char))
213 if (
auto result = consume_c_float(str); !result.first.empty())
216 if (
auto result = consume_c_unsigned(str); !result.first.empty())
219 if (
auto result = consume_c_integer(str); !result.first.empty())
227 template <
char DELIMITER = '\''>
228 [[nodiscard]]
inline std::pair<std::string_view, std::string> consume_c_string(std::string_view& strv)
230 if (strv.empty() || strv[0] != DELIMITER)
231 throw std::runtime_error(
"C string must start with delimiter");
233 std::pair<std::string_view, std::string> result;
236 auto start = view.begin();
237 view.remove_prefix(1);
238 while (view[0] != DELIMITER)
245 throw std::runtime_error(
"unterminated C string");
249 case 'n': result.second +=
'\n';
break;
250 case '"': result.second +=
'"';
break;
251 case '\'': result.second +=
'\'';
break;
252 case '\\': result.second +=
'\\';
break;
253 case '/': result.second +=
'/';
break;
254 case 'b': result.second +=
'\b';
break;
255 case 'r': result.second +=
'\r';
break;
256 case 'f': result.second +=
'\f';
break;
257 case 't': result.second +=
'\t';
break;
258 case '0': result.second +=
'\0';
break;
262 if (num.size() < 3 || view.empty())
return {};
264 auto parsed = consume_c_integer(num, 8);
265 if (parsed.first.empty() || !num.empty())
return {};
267 if (parsed.second > 255)
return {};
268 result.second.push_back((
char)parsed.second);
274 if (num.size() < 2 || view.empty())
return {};
276 auto parsed = consume_c_integer(num, 16);
277 if (parsed.first.empty() || !num.empty())
return {};
280 result.second += (char)parsed.second;
286 if (num.size() < 4 || view.empty())
return {};
288 auto parsed = consume_c_integer(num, 16);
289 if (parsed.first.empty() || !num.empty())
return {};
291 append_utf8(result.second, (
char32_t)parsed.second);
297 if (num.size() < 8 || view.empty())
return {};
299 auto parsed = consume_c_integer(num, 16);
300 if (parsed.first.empty() || !num.empty())
return {};
302 append_utf8(result.second, (
char32_t)parsed.second);
306 throw std::runtime_error(
"unknown escape character");
315 throw std::runtime_error(
"unterminated C string");
319 throw std::runtime_error(
"C string must end with delimiter");
321 result.first = make_sv(start, view.begin());
326 inline bool try_eat(std::string_view& str, std::string_view what)
328 string_ops::trim_whitespace_left(str);
329 if (!str.starts_with(what))
331 str.remove_prefix(what.size());
335 inline bool try_eat(std::string_view& str,
char what)
337 string_ops::trim_whitespace_left(str);
338 if (!str.starts_with(what))
340 str.remove_prefix(1);
344 inline void eat(std::string_view& str, std::string_view what)
346 if (!try_eat(str, what))
350 inline void eat(std::string_view& str,
char what)
352 if (!try_eat(str, what))
356 inline std::string_view try_eat_identifier(std::string_view& str)
358 string_ops::trim_whitespace_left(str);
362 inline std::string_view eat_identifier(std::string_view& str)
364 const auto result = try_eat_identifier(str);
370 inline std::string_view try_eat_identifier_with(std::string_view& str, std::string_view additional_chars)
372 string_ops::trim_whitespace_left(str);
376 inline std::string_view eat_identifier_with(std::string_view& str, std::string_view additional_chars)
378 const auto result = try_eat_identifier_with(str, additional_chars);
384 inline std::string_view eat_whitespace(std::string_view& str)
389 inline bool try_eat_line_comment(std::string_view& str, std::string_view comment_start =
"//")
391 string_ops::trim_whitespace_left(str);
392 if (!try_eat(str, comment_start))
398 inline bool try_eat_unsigned(std::string_view& str, uint64_t& result,
int base = 10)
400 string_ops::trim_whitespace_left(str);
401 auto [parsed, value] = consume_c_unsigned(str, base);
402 if (parsed.empty())
return false;
407 inline std::optional<uint64_t> try_eat_unsigned(std::string_view& str,
int base = 10)
409 if (uint64_t result = 0; try_eat_unsigned(str, result, base))
414 inline bool try_eat_integer(std::string_view& str, int64_t& result,
int base = 10)
416 string_ops::trim_whitespace_left(str);
417 auto [parsed, value] = consume_c_integer(str, base);
418 if (parsed.empty())
return false;
423 inline std::optional<int64_t> try_eat_integer(std::string_view& str,
int base = 10)
425 if (int64_t result = 0; try_eat_integer(str, result, base))
430 inline bool try_eat_float(std::string_view& str,
double& result)
432 string_ops::trim_whitespace_left(str);
433 auto [parsed, value] = consume_c_float(str);
434 if (parsed.empty())
return false;
439 inline std::optional<double> try_eat_float(std::string_view& str)
441 if (
double result = 0; try_eat_float(str, result))
446 inline uint64_t eat_unsigned(std::string_view& str,
int base = 10)
449 if (!try_eat_unsigned(str, result, base))
450 throw parse_error(str,
"expected unsigned integer of base {}", base);
454 inline int64_t eat_integer(std::string_view& str,
int base = 10)
457 if (!try_eat_integer(str, result, base))
458 throw parse_error(str,
"expected integer of base {}", base);
465 if (!try_eat_float(str, result))
466 throw parse_error(str,
"expected floating-point number");
470 inline char32_t try_eat_utf8_codepoint(std::string_view& str)
475 inline char32_t eat_utf8_codepoint(std::string_view& str)
479 throw parse_error(str,
"expected UTF-8 codepoint");
485 template <GHPL_FORMAT_TEMPLATE>
486 [[nodiscard]]
inline parsing::parse_error parse_error(token_range
const& range, GHPL_FORMAT_ARGS) {
return parsing::parse_error(to_string_view(range), GHPL_FORMAT_FORWARD); }
487 template <GHPL_FORMAT_TEMPLATE>
488 [[nodiscard]]
inline parsing::parse_error parse_error(token_it
const& it, GHPL_FORMAT_ARGS) {
return parsing::parse_error(it->range, GHPL_FORMAT_FORWARD); }
492 virtual ~term() noexcept = default;
493 token_range source_range;
495 term(token_it it) : source_range(it, std::next(it)) {}
496 term(token_range range) : source_range(range) {}
499 struct function_call_expression :
public term
501 std::vector<std::unique_ptr<term>> arguments;
503 static std::string make_name(std::span<token_it const> name_parts,
bool infix)
508 for (token_it it : name_parts)
515 function_call_expression(token_range range, std::span<token_it const> name_, std::vector<std::unique_ptr<term>> arguments_,
bool infix)
517 , name(make_name(name_, infix))
518 , arguments(std::move(arguments_))
523 struct identifier_expression :
public term
525 std::string identifier;
527 identifier_expression(token_it it) : term(it), identifier(it->range) {}
530 struct literal_expression :
public term
534 literal_expression(token_it it) : term(it), literal(*it) {}
537 inline std::unique_ptr<term> parse_expression(token_range& tokens)
539 std::unique_ptr<term> result;
541 auto start = tokens.begin();
543 std::vector<std::unique_ptr<term>> constituents;
544 while (tokens && tokens.front().type >= token::word)
546 if (tokens.front().type == token::word)
548 constituents.push_back(std::make_unique<identifier_expression>(tokens.begin()));
551 else if (tokens.front().type == token::number || tokens.front().type == token::string)
553 constituents.push_back(std::make_unique<literal_expression>(tokens.begin()));
556 else if (tokens.front().type == token::start_sub_expression)
559 constituents.push_back(parse_expression(tokens));
561 if (tokens.front().type != token::end_sub_expression)
562 throw parse_error(tokens.begin(),
"unexpected end of line");
566 throw parse_error(tokens.begin(),
"expected expression part");
568 if (constituents.empty())
569 throw parse_error(tokens.begin(),
"empty expression encountered");
571 const auto constitutent_count = constituents.size();
572 if (constitutent_count == 1)
573 return std::move(constituents[0]);
575 const bool infix = (constitutent_count % 2) == 1;
577 std::vector<token_it> function_name;
578 std::vector<std::unique_ptr<term>> arguments;
580 arguments.push_back(std::exchange(constituents[0], {}));
582 for (
size_t i = infix; i < constitutent_count; i += 2)
584 auto& function_identifier = constituents[i];
585 if (
auto identifier =
dynamic_cast<identifier_expression*
>(function_identifier.get()))
587 function_name.push_back(identifier->source_range.begin());
588 arguments.push_back(std::exchange(constituents[i + 1], {}));
591 throw parse_error(function_identifier->source_range,
"expected function name part");
594 return std::make_unique<function_call_expression>(std::ranges::subrange(start, tokens.begin()), std::move(function_name), std::move(arguments), infix);
597 inline std::unique_ptr<term> parse_expression(std::string_view& str)
599 const auto tokens = lex(str);
600 using tokenit = std::ranges::iterator_t<
decltype(tokens)>;
601 token_range range = tokens;
602 return parse_expression(range);