1 //===-- Format string parser for printf -------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_PARSER_H 10 #define LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_PARSER_H 11 12 #include "core_structs.h" 13 #include "hdr/types/struct_tm.h" 14 #include "src/__support/CPP/string_view.h" 15 #include "src/__support/ctype_utils.h" 16 #include "src/__support/macros/config.h" 17 #include "src/__support/str_to_integer.h" 18 19 namespace LIBC_NAMESPACE_DECL { 20 namespace strftime_core { 21 22 class Parser { 23 const char *str; 24 size_t cur_pos = 0; 25 26 public: Parser(const char * new_str)27 LIBC_INLINE Parser(const char *new_str) : str(new_str) {} 28 29 // get_next_section will parse the format string until it has a fully 30 // specified format section. This can either be a raw format section with no 31 // conversion, or a format section with a conversion that has all of its 32 // variables stored in the format section. get_next_section()33 LIBC_INLINE FormatSection get_next_section() { 34 FormatSection section; 35 size_t starting_pos = cur_pos; 36 37 if (str[cur_pos] != '%') { 38 // raw section 39 section.has_conv = false; 40 while (str[cur_pos] != '%' && str[cur_pos] != '\0') 41 ++cur_pos; 42 section.raw_string = {str + starting_pos, cur_pos - starting_pos}; 43 return section; 44 } 45 46 // format section 47 section.has_conv = true; 48 ++cur_pos; 49 50 // flags 51 section.flags = parse_flags(&cur_pos); 52 53 // handle width 54 section.min_width = 0; 55 if (internal::isdigit(str[cur_pos])) { 56 auto result = internal::strtointeger<int>(str + cur_pos, 10); 57 section.min_width = result.value; 58 cur_pos = cur_pos + result.parsed_len; 59 } 60 61 // modifiers 62 switch (str[cur_pos]) { 63 case ('E'): 64 section.modifier = ConvModifier::E; 65 ++cur_pos; 66 break; 67 case ('O'): 68 section.modifier = ConvModifier::O; 69 ++cur_pos; 70 break; 71 default: 72 section.modifier = ConvModifier::none; 73 } 74 75 section.conv_name = str[cur_pos]; 76 77 // If the end of the format section is on the '\0'. This means we need to 78 // not advance the cur_pos. 79 if (str[cur_pos] != '\0') 80 ++cur_pos; 81 82 section.raw_string = {str + starting_pos, cur_pos - starting_pos}; 83 return section; 84 } 85 86 private: parse_flags(size_t * local_pos)87 LIBC_INLINE FormatFlags parse_flags(size_t *local_pos) { 88 bool found_flag = true; 89 FormatFlags flags = FormatFlags(0); 90 while (found_flag) { 91 switch (str[*local_pos]) { 92 case '+': 93 flags = static_cast<FormatFlags>(flags | FormatFlags::FORCE_SIGN); 94 break; 95 case '0': 96 flags = static_cast<FormatFlags>(flags | FormatFlags::LEADING_ZEROES); 97 break; 98 default: 99 found_flag = false; 100 } 101 if (found_flag) 102 ++*local_pos; 103 } 104 return flags; 105 } 106 }; 107 108 } // namespace strftime_core 109 } // namespace LIBC_NAMESPACE_DECL 110 111 #endif // LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_PARSER_H 112