13 using namespace string_ops;
50 [[
nodiscard]]
inline std::string_view consume_c_identifier(std::string_view& str)
52 if (str.empty() || !ascii::isidentstart(str[0]))
55 const auto start = str.begin();
57 trim_while(str, ascii::isident);
58 return make_sv(start, str.begin());
66 const auto start = str.begin();
69 return make_sv(start, str.begin());
76 if (str.empty() || !(ascii::isdigit(str[0]) || str[0] ==
'-'))
79 std::pair<std::string_view, double> result;
83 return { {}, std::numeric_limits<double>::quiet_NaN() };
86 str.remove_prefix(result.first.size());
92 if (str.empty() || !(ascii::isdigit(str[0]) || str[0] ==
'-'))
95 std::pair<std::string_view, int64_t> result;
102 str.remove_prefix(result.first.size());
108 if (str.empty() || !ascii::isdigit(str[0]))
111 std::pair<std::string_view, uint64_t> result;
113 const auto from_chars_result = std::from_chars(str.data(), str.data() + str.size(), result.second, base);
118 str.remove_prefix(result.first.size());
122 template <
char DELIMITER = '\''>
126 throw std::runtime_error(
"C string must start with delimiter");
128 std::pair<std::string_view, std::string> result;
131 auto start =
view.begin();
132 view.remove_prefix(1);
140 throw std::runtime_error(
"unterminated C string");
144 case 'n': result.second +=
'\n';
break;
145 case '"': result.second +=
'"';
break;
146 case '\'': result.second +=
'\'';
break;
147 case '\\': result.second +=
'\\';
break;
148 case 'b': result.second +=
'\b';
break;
149 case 'r': result.second +=
'\r';
break;
150 case 'f': result.second +=
'\f';
break;
151 case 't': result.second +=
'\t';
break;
152 case '0': result.second +=
'\0';
break;
156 if (
num.size() < 3 ||
view.empty())
return {};
159 if (
parsed.first.empty() || !
num.empty())
return {};
161 if (
parsed.second > 255)
return {};
162 result.second.push_back((
char)
parsed.second);
168 if (
num.size() < 2 ||
view.empty())
return {};
171 if (
parsed.first.empty() || !
num.empty())
return {};
180 if (
num.size() < 4 ||
view.empty())
return {};
183 if (
parsed.first.empty() || !
num.empty())
return {};
191 if (
num.size() < 8 ||
view.empty())
return {};
194 if (
parsed.first.empty() || !
num.empty())
return {};
200 throw std::runtime_error(
"unknown escape character");
209 throw std::runtime_error(
"unterminated C string");
213 throw std::runtime_error(
"C string must end with delimiter");
215 result.first = make_sv(start,
view.begin());
220 [[
deprecated(
"WARNING: This function is incomplete")]]
221 [[
nodiscard]]
inline std::tuple<std::string_view, std::variant<double, uint64_t, int64_t>>
consume_c_number(std::string_view& str)
254 struct parse_error : std::runtime_error
256 std::string_view Where;
258 template <GHPL_FORMAT_TEMPLATE>
259 parse_error(std::string_view
where, GHPL_FORMAT_ARGS)
260 : runtime_error(GHPL_FORMAT_CALL)
267 inline bool try_eat(std::string_view& str, std::string_view
what)
269 string_ops::trim_whitespace_left(str);
270 if (!str.starts_with(
what))
272 str.remove_prefix(
what.size());
276 inline bool try_eat(std::string_view& str,
char what)
278 string_ops::trim_whitespace_left(str);
279 if (!str.starts_with(
what))
281 str.remove_prefix(1);
285 inline void eat(std::string_view& str, std::string_view
what)
287 if (!try_eat(str,
what))
288 throw parse_error(str,
"expected '{}'",
what);
291 inline void eat(std::string_view& str,
char what)
293 if (!try_eat(str,
what))
294 throw parse_error(str,
"expected '{}'",
what);
297 inline std::string_view try_eat_identifier(std::string_view& str)
299 string_ops::trim_whitespace_left(str);
300 return consume_c_identifier(str);
303 inline std::string_view eat_identifier(std::string_view& str)
305 const auto result = try_eat_identifier(str);
307 throw parse_error(str,
"expected identifier");
311 inline std::string_view try_eat_identifier_with(std::string_view& str, std::string_view
additional_chars)
313 string_ops::trim_whitespace_left(str);
317 inline std::string_view eat_identifier_with(std::string_view& str, std::string_view
additional_chars)
321 throw parse_error(str,
"expected identifier");
325 inline std::string_view eat_whitespace(std::string_view& str)
330 inline bool try_eat_line_comment(std::string_view& str, std::string_view
comment_start =
"//")
332 string_ops::trim_whitespace_left(str);
339 inline bool try_eat_unsigned(std::string_view& str,
uint64_t& result,
int base = 10)
341 string_ops::trim_whitespace_left(str);
343 if (
parsed.empty())
return false;
348 inline std::optional<uint64_t> try_eat_unsigned(std::string_view& str,
int base = 10)
350 if (
uint64_t result = 0; try_eat_unsigned(str, result, base))
355 inline bool try_eat_integer(std::string_view& str,
int64_t& result,
int base = 10)
357 string_ops::trim_whitespace_left(str);
359 if (
parsed.empty())
return false;
364 inline std::optional<int64_t> try_eat_integer(std::string_view& str,
int base = 10)
366 if (
int64_t result = 0; try_eat_integer(str, result, base))
371 inline uint64_t eat_unsigned(std::string_view& str,
int base = 10)
374 if (!try_eat_unsigned(str, result, base))
375 throw parse_error(str,
"expected unsigned integer of base {}", base);
379 inline int64_t eat_integer(std::string_view& str,
int base = 10)
382 if (!try_eat_integer(str, result, base))
383 throw parse_error(str,
"expected integer of base {}", base);
387 inline char32_t try_eat_utf8_codepoint(std::string_view& str)
392 inline char32_t eat_utf8_codepoint(std::string_view& str)
396 throw parse_error(str,
"expected UTF-8 codepoint");
402 template <GHPL_FORMAT_TEMPLATE>
403 [[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); }
404 template <GHPL_FORMAT_TEMPLATE>
405 [[nodiscard]]
inline parsing::parse_error parse_error(token_it
const& it, GHPL_FORMAT_ARGS) {
return parsing::parse_error(it->range, GHPL_FORMAT_FORWARD); }
409 virtual ~expression()
noexcept =
default;
410 token_range source_range;
412 expression(token_it it) : source_range(it, std::next(it)) {}
413 expression(token_range range) : source_range(range) {}
416 struct function_call_expression :
public expression
418 std::vector<std::unique_ptr<expression>> arguments;
420 static std::string make_name(std::span<token_it const> name_parts,
bool infix)
425 for (token_it it : name_parts)
432 function_call_expression(token_range range, std::span<token_it const> name_, std::vector<std::unique_ptr<expression>> arguments_,
bool infix)
434 , name(make_name(name_, infix))
435 , arguments(std::move(arguments_))
440 struct identifier_expression :
public expression
442 std::string identifier;
444 identifier_expression(token_it it) : expression(it), identifier(it->range) {}
447 struct literal_expression :
public expression
451 literal_expression(token_it it) : expression(it), literal(*it) {}
454 inline std::unique_ptr<expression> parse_expression(token_range& tokens)
456 std::unique_ptr<expression> result;
458 auto start = tokens.begin();
460 std::vector<std::unique_ptr<expression>> constituents;
461 while (tokens && tokens.front().type >= token::word)
463 if (tokens.front().type == token::word)
465 constituents.push_back(std::make_unique<identifier_expression>(tokens.begin()));
468 else if (tokens.front().type == token::number || tokens.front().type == token::string)
470 constituents.push_back(std::make_unique<literal_expression>(tokens.begin()));
473 else if (tokens.front().type == token::start_sub_expression)
476 constituents.push_back(parse_expression(tokens));
478 if (tokens.front().type != token::end_sub_expression)
479 throw parse_error(tokens.begin(),
"unexpected end of line");
483 throw parse_error(tokens.begin(),
"expected expression part");
485 if (constituents.empty())
486 throw parse_error(tokens.begin(),
"empty expression encountered");
488 const auto constitutent_count = constituents.size();
489 if (constitutent_count == 1)
490 return std::move(constituents[0]);
492 const bool infix = (constitutent_count % 2) == 1;
494 std::vector<token_it> function_name;
495 std::vector<std::unique_ptr<expression>> arguments;
497 arguments.push_back(std::exchange(constituents[0], {}));
499 for (
size_t i = infix; i < constitutent_count; i += 2)
501 auto& function_identifier = constituents[i];
502 if (
auto identifier =
dynamic_cast<identifier_expression*
>(function_identifier.get()))
504 function_name.push_back(identifier->source_range.begin());
505 arguments.push_back(std::exchange(constituents[i + 1], {}));
508 throw parse_error(function_identifier->source_range,
"expected function name part");
511 return std::make_unique<function_call_expression>(std::ranges::subrange(start, tokens.begin()), std::move(function_name), std::move(arguments), infix);
514 inline std::unique_ptr<expression> parse_expression(std::string_view& str)
516 const auto tokens = lex(str);
517 using tokenit = std::ranges::iterator_t<
decltype(tokens)>;
518 token_range range = tokens;
519 return parse_expression(range);
constexpr auto bit_count
Equal to the number of bits in the type.
constexpr __contains_fn contains
contains(range, el)
std::string_view consume_while(std::string_view &str, FUNC &&pred)
Consumes characters from the beginning of str while they match pred(str[0]).
auto from_chars(std::string_view str, T &value, const int base=10) noexcept
A version of std::from_chars that takes a std::string_view as the first argument.
std::string_view consume_until(std::string_view &str, FUNC &&pred)
Consumes characters from the beginning of str until one matches pred(str[0]), exclusive.
char consume(std::string_view &str)
Consumes and returns the first character in the str, or \0 if no more characters.
std::string_view consume_n(std::string_view &str, size_t n)
Consumes at most n characters from the beginning of str.
constexpr size_t append_utf8(string8 auto &buffer, char32_t cp)
Appends octets to buffer by encoding cp into UTF-8.
constexpr char32_t consume_utf8(string_view8 auto &str)
Consumes (see consume()) a UTF-8 codepoint from str.