From: Jacob Lifshay Date: Fri, 30 Jun 2017 12:29:40 +0000 (-0700) Subject: working on refactoring generate_spirv_parser X-Git-Tag: gsoc-2017~73^2~16 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=ce2bbedf5ff57ce5e55ef5bce24b5e26803bcb9d;p=kazan.git working on refactoring generate_spirv_parser --- diff --git a/src/generate_spirv_parser/generate.cpp b/src/generate_spirv_parser/generate.cpp index 5ca92b3..c1a05c6 100644 --- a/src/generate_spirv_parser/generate.cpp +++ b/src/generate_spirv_parser/generate.cpp @@ -22,15 +22,14 @@ */ #include "generate.h" #include "json/json.h" -#include "util/optional.h" -#include "util/filesystem.h" +#include +#include #include -#include -#include -#include -#include -#include +#include #include +#include +#include +#include namespace vulkan_cpu { @@ -38,515 +37,270 @@ namespace generate_spirv_parser { namespace generate { -using namespace util::string_view_literals; - -struct Spirv_and_parser_generator : public Generator -{ - struct State; - virtual void run(Generator_args &generator_args, - const ast::Top_level &top_level) const override; -}; +constexpr std::size_t detail::Generated_output_stream::output_tab_width_no_tabs_allowed; +constexpr util::string_view detail::Generated_output_stream::literal_command; +constexpr char detail::Generated_output_stream::indent_indicator_char; +constexpr char detail::Generated_output_stream::literal_indent_indicator_char; +constexpr std::size_t detail::Generated_output_stream::indent_indicators_per_indent; +constexpr char detail::Generated_output_stream::escape_char; +constexpr bool detail::Generated_output_stream::indent_blank_lines; -namespace +void detail::Generated_output_stream::write_to_file(bool do_reindent) const { -} - -struct Spirv_and_parser_generator::State -{ - class Generated_output_stream + std::ofstream os; + os.exceptions(std::ios::badbit); + os.open(file_path.c_str()); + if(!os) + throw util::filesystem::filesystem_error( + "open failed", file_path, std::make_error_code(std::io_errc::stream)); + os.exceptions(std::ios::badbit | std::ios::failbit); + if(do_reindent) { - private: - std::deque value; - util::filesystem::path file_path; - - public: - explicit Generated_output_stream(util::filesystem::path file_path) noexcept - : value(), - file_path(std::move(file_path)) - { - } - const util::filesystem::path &get_file_path() const noexcept - { - return file_path; - } - static constexpr std::size_t output_tab_width_no_tabs_allowed = 0; - static constexpr util::string_view literal_command = "literal:"_sv; - template - static void write_indent(Fn write_char, - std::size_t indent_depth, - std::size_t output_tab_width = output_tab_width_no_tabs_allowed) - { - if(output_tab_width != output_tab_width_no_tabs_allowed) + auto iter = value.begin(); + bool is_at_start_of_line = true; + std::size_t start_indent_depth = 0; + std::size_t indent_depth = 0; + constexpr std::size_t output_indent_width = 4; + constexpr std::size_t output_tab_width = output_tab_width_no_tabs_allowed; + while(iter != value.end()) + { + if(*iter == '\n') { - while(indent_depth >= output_tab_width) - { - indent_depth -= output_tab_width; - write_char('\t'); - } + if(indent_blank_lines && is_at_start_of_line) + write_indent( + [&](char ch) + { + os << ch; + }, + indent_depth, + output_tab_width); + is_at_start_of_line = true; + indent_depth = start_indent_depth; + os << *iter++; } - while(indent_depth--) - write_char(' '); - } - static constexpr char indent_indicator_char = ' '; - static constexpr char literal_indent_indicator_char = '`'; - static constexpr std::size_t indent_indicators_per_indent = 4; - static constexpr char escape_char = '@'; - static constexpr bool indent_blank_lines = false; - void write_to_file(bool do_reindent = true) const - { - std::ofstream os; - os.exceptions(std::ios::badbit); - os.open(file_path.c_str()); - if(!os) - throw util::filesystem::filesystem_error( - "open failed", file_path, std::make_error_code(std::io_errc::stream)); - os.exceptions(std::ios::badbit | std::ios::failbit); - if(do_reindent) + else if(is_at_start_of_line) { - auto iter = value.begin(); - bool is_at_start_of_line = true; - std::size_t start_indent_depth = 0; - std::size_t indent_depth = 0; - constexpr std::size_t output_indent_width = 4; - constexpr std::size_t output_tab_width = output_tab_width_no_tabs_allowed; - while(iter != value.end()) + switch(*iter) { - if(*iter == '\n') + case '\r': + case '\t': + case '\f': + case '\0': + assert(false); + continue; + case literal_indent_indicator_char: + ++iter; + indent_depth++; + continue; + case indent_indicator_char: + for(std::size_t i = 0; i < indent_indicators_per_indent; i++) { - if(indent_blank_lines && is_at_start_of_line) - write_indent( - [&](char ch) - { - os << ch; - }, - indent_depth, - output_tab_width); - is_at_start_of_line = true; - indent_depth = start_indent_depth; - os << *iter++; + assert(iter != value.end()); + assert(*iter == indent_indicator_char); + ++iter; } - else if(is_at_start_of_line) + indent_depth += output_indent_width; + continue; + case escape_char: + { + ++iter; + assert(iter != value.end()); + if(*iter != escape_char) { - switch(*iter) + if(*iter >= 'a' && *iter <= 'z') { - case '\r': - case '\t': - case '\f': - case '\0': - assert(false); - continue; - case literal_indent_indicator_char: - ++iter; - indent_depth++; - continue; - case indent_indicator_char: - for(std::size_t i = 0; i < indent_indicators_per_indent; i++) + std::string command; + while(true) { assert(iter != value.end()); - assert(*iter == indent_indicator_char); - ++iter; + if(*iter == escape_char) + break; + command += *iter++; } - indent_depth += output_indent_width; - continue; - case escape_char: - { - ++iter; assert(iter != value.end()); - if(*iter != escape_char) + assert(*iter == escape_char); + ++iter; + auto command_sv = util::string_view(command); + if(command_sv.compare(0, literal_command.size(), literal_command) == 0) { - if(*iter >= 'a' && *iter <= 'z') + auto arg = command_sv.substr(literal_command.size()); + std::size_t count = 0; + do { - std::string command; - while(true) - { - assert(iter != value.end()); - if(*iter == escape_char) - break; - command += *iter++; - } - assert(iter != value.end()); - assert(*iter == escape_char); - ++iter; - auto command_sv = util::string_view(command); - if(command_sv.compare( - 0, literal_command.size(), literal_command) - == 0) - { - auto arg = command_sv.substr(literal_command.size()); - std::size_t count = 0; - do - { - count *= 10; - assert(!arg.empty() && arg.front() >= '0' - && arg.front() <= '9'); - count += arg.front() - '0'; - arg.remove_prefix(1); - } while(!arg.empty()); - write_indent( - [&](char ch) - { - os << ch; - }, - indent_depth, - output_tab_width); - indent_depth = 0; - for(std::size_t i = 0; i < count; i++) - { - assert(iter != value.end()); - os << *iter++; - } - assert(iter != value.end() && *iter == escape_char); - ++iter; - continue; - } - else + count *= 10; + assert(!arg.empty() && arg.front() >= '0' + && arg.front() <= '9'); + count += arg.front() - '0'; + arg.remove_prefix(1); + } while(!arg.empty()); + write_indent( + [&](char ch) { - assert(false); - } - } - switch(*iter) + os << ch; + }, + indent_depth, + output_tab_width); + indent_depth = 0; + for(std::size_t i = 0; i < count; i++) { - case '-': - ++iter; - assert(start_indent_depth >= output_indent_width); - assert(indent_depth >= output_indent_width); - start_indent_depth -= output_indent_width; - indent_depth -= output_indent_width; - continue; - case '+': - ++iter; - start_indent_depth += output_indent_width; - indent_depth += output_indent_width; - continue; + assert(iter != value.end()); + os << *iter++; } - assert(false); + assert(iter != value.end() && *iter == escape_char); + ++iter; continue; } - break; + else + { + assert(false); + } } + switch(*iter) + { + case '-': + ++iter; + assert(start_indent_depth >= output_indent_width); + assert(indent_depth >= output_indent_width); + start_indent_depth -= output_indent_width; + indent_depth -= output_indent_width; + continue; + case '_': + ++iter; + assert(start_indent_depth >= output_indent_width); + start_indent_depth -= output_indent_width; + continue; + case '+': + ++iter; + start_indent_depth += output_indent_width; + indent_depth += output_indent_width; + continue; } - write_indent( - [&](char ch) - { - os << ch; - }, - indent_depth, - output_tab_width); - is_at_start_of_line = false; - os << *iter++; - } - else - { - os << *iter++; + assert(false); + continue; } + break; } + } + write_indent( + [&](char ch) + { + os << ch; + }, + indent_depth, + output_tab_width); + is_at_start_of_line = false; + os << *iter++; } else { - for(char ch : value) - os << ch; - } - os.close(); // manually close to not hide error exceptions - } - void write_unsigned_integer(std::uint64_t value, - unsigned base = json::ast::Number_value::default_base, - std::size_t min_length = 1) - { - static_assert(std::numeric_limits::radix == 2, ""); - constexpr std::size_t buffer_size = std::numeric_limits::digits; - char buffer[buffer_size]; - while(min_length > buffer_size) - { - *this << '0'; - min_length--; + os << *iter++; } - std::size_t length = json::ast::Number_value::unsigned_integer_to_buffer( - value, buffer, buffer_size, false, base, min_length); - *this << util::string_view(buffer, length); } - void write_signed_integer(std::int64_t value, - unsigned base = json::ast::Number_value::default_base) - { - static_assert(std::numeric_limits::radix == 2, ""); - constexpr std::size_t buffer_size = - std::numeric_limits::digits + 1; // one extra for sign - char buffer[buffer_size]; - std::size_t length = json::ast::Number_value::signed_integer_to_buffer( - value, buffer, buffer_size, false, base); - *this << util::string_view(buffer, length); - } - void write_literal(util::string_view value) - { - *this << escape_char; - *this << literal_command; - write_unsigned_integer(value.size()); - *this << escape_char; - *this << value; - *this << escape_char; - } - template - Generated_output_stream &operator<<(T) = delete; - Generated_output_stream &operator<<(char ch) - { - value.push_back(ch); - return *this; - } - Generated_output_stream &operator<<(util::string_view sv) - { - for(char ch : sv) - *this << ch; - return *this; - } - Generated_output_stream &operator<<(const char *s) - { - return operator<<(util::string_view(s)); - } - Generated_output_stream &operator<<(const std::string &s) - { - return operator<<(util::string_view(s)); - } - Generated_output_stream &operator<<(std::uint64_t v) - { - write_unsigned_integer(v); - return *this; - } - Generated_output_stream &operator<<(std::int64_t v) - { - write_signed_integer(v); - return *this; - } - Generated_output_stream &operator<<(const ast::Copyright &v) - { - *this << "/*\n"; - for(auto &line : v.lines) - { - if(line.empty()) - { - *this << "`*\n"; - continue; - } - *this << "`* "; - bool was_last_star = false; - for(char ch : line) - { - if(was_last_star && ch == '/') - *this << ' '; - was_last_star = (ch == '*'); - *this << ch; - } - *this << "\n"; - } - *this << "`*/\n"; - return *this; - } - }; - struct Literal_holder - { - util::string_view value; - friend Generated_output_stream &operator<<(Generated_output_stream &os, - const Literal_holder &v) - { - os.write_literal(v.value); - return os; - } - }; - static Literal_holder literal(util::string_view value) - { - return Literal_holder{value}; } - struct Unsigned_integer_holder - { - std::uint64_t value; - unsigned base; - std::size_t min_length; - friend Generated_output_stream &operator<<(Generated_output_stream &os, - const Unsigned_integer_holder &v) - { - os.write_unsigned_integer(v.value, v.base, v.min_length); - return os; - } - }; - static Unsigned_integer_holder unsigned_integer( - std::uint64_t value, - unsigned base = json::ast::Number_value::default_base, - std::size_t min_length = 1) + else { - return Unsigned_integer_holder{value, base, min_length}; + for(char ch : value) + os << ch; } - struct Signed_integer_holder - { - std::int64_t value; - unsigned base; - friend Generated_output_stream &operator<<(Generated_output_stream &os, - const Signed_integer_holder &v) - { - os.write_unsigned_integer(v.value, v.base); - return os; - } - }; - static Signed_integer_holder signed_integer( - std::int64_t value, unsigned base = json::ast::Number_value::default_base) + os.close(); // manually close to not hide error exceptions +} + +void detail::Generated_output_stream::write_unsigned_integer(std::uint64_t value, + unsigned base, + std::size_t min_length) +{ + static_assert(std::numeric_limits::radix == 2, ""); + constexpr std::size_t buffer_size = std::numeric_limits::digits; + char buffer[buffer_size]; + while(min_length > buffer_size) { - return Signed_integer_holder{value, base}; + *this << '0'; + min_length--; } - class Word_iterator - { - public: - typedef std::ptrdiff_t difference_type; - typedef util::string_view value_type; - typedef const util::string_view &reference; - typedef const util::string_view *pointer; - typedef std::input_iterator_tag iterator_category; - - private: - enum class Char_class - { - uppercase, - other_identifier, - word_separator - }; - static constexpr Char_class get_char_class(char ch) noexcept - { - if(ch >= 'A' && ch <= 'Z') - return Char_class::uppercase; - if(ch >= 'a' && ch <= 'z') - return Char_class::other_identifier; - if(ch >= '0' && ch <= '9') - return Char_class::other_identifier; - return Char_class::word_separator; - } + std::size_t length = json::ast::Number_value::unsigned_integer_to_buffer( + value, buffer, buffer_size, false, base, min_length); + *this << util::string_view(buffer, length); +} - private: - util::string_view word; - util::string_view words; +void detail::Generated_output_stream::write_signed_integer(std::int64_t value, unsigned base) +{ + static_assert(std::numeric_limits::radix == 2, ""); + constexpr std::size_t buffer_size = + std::numeric_limits::digits + 1; // one extra for sign + char buffer[buffer_size]; + std::size_t length = + json::ast::Number_value::signed_integer_to_buffer(value, buffer, buffer_size, false, base); + *this << util::string_view(buffer, length); +} - private: - constexpr void next() noexcept - { - util::optional word_start; - Char_class last_char_class = Char_class::word_separator; - for(std::size_t i = 0; i < words.size(); i++) - { - auto current_char_class = get_char_class(words[i]); - if(word_start) - { - switch(current_char_class) - { - case Char_class::word_separator: - word = util::string_view(words.data() + *word_start, i - *word_start); - words.remove_prefix(i); - last_char_class = current_char_class; - return; - case Char_class::uppercase: - if(last_char_class != Char_class::uppercase) - { - word = util::string_view(words.data() + *word_start, i - *word_start); - words.remove_prefix(i); - last_char_class = current_char_class; - return; - } - if(i + 1 < words.size() - && get_char_class(words[i + 1]) == Char_class::other_identifier) - { - word = util::string_view(words.data() + *word_start, i - *word_start); - words.remove_prefix(i); - last_char_class = current_char_class; - return; - } - break; - case Char_class::other_identifier: - break; - } - } - else if(current_char_class != Char_class::word_separator) - { - word_start = i; - } - last_char_class = current_char_class; - } - if(word_start) - word = util::string_view(words.data() + *word_start, words.size() - *word_start); - else - word = {}; - words = {}; - } - constexpr bool at_end() const noexcept - { - return word.empty(); - } +detail::Generated_output_stream &detail::Generated_output_stream::operator<<(Guard_macro) +{ + *this << name_from_words_all_uppercase_with_trailing_underline(get_file_path().string()); + return *this; +} - public: - constexpr Word_iterator() noexcept : word(), words() - { - } - constexpr explicit Word_iterator(util::string_view words) noexcept : word(), words(words) - { - next(); - } - constexpr const util::string_view &operator*() const noexcept - { - return word; - } - constexpr const util::string_view *operator->() const noexcept - { - return &word; - } - constexpr Word_iterator &operator++() noexcept - { - next(); - return *this; - } - constexpr Word_iterator operator++(int) noexcept - { - auto retval = *this; - next(); - return retval; - } - constexpr bool operator==(const Word_iterator &rt) const noexcept - { - return word.empty() == rt.word.empty(); - } - constexpr bool operator!=(const Word_iterator &rt) const noexcept - { - return word.empty() != rt.word.empty(); - } - constexpr Word_iterator begin() const noexcept - { - return *this; - } - constexpr Word_iterator end() const noexcept - { - return {}; - } - }; - static void write_guard_macro(Generated_output_stream &os) +std::string detail::Generated_output_stream::name_from_words_helper(Name_format name_format, + std::string name) +{ + for(char &ch : name) + if(ch >= 'A' && ch <= 'Z') + ch = ch - 'A' + 'a'; // to lowercase + if(name.empty() || (name[0] >= '0' && name[0] <= '9')) + name.insert(0, 1, '_'); + bool has_trailing_underline = false; + switch(name_format) { - auto path_string = os.get_file_path().string(); - for(auto &word : Word_iterator(path_string)) + case initial_capital: + // can't be empty because of previous insert + if(name[0] >= 'a' && name[0] <= 'z') + name[0] = name[0] - 'a' + 'A'; // to uppercase + break; + case all_uppercase_with_trailing_underline: + case all_uppercase: + if(name_format == all_uppercase_with_trailing_underline) + has_trailing_underline = true; + for(char &ch : name) + if(ch >= 'a' && ch <= 'z') + ch = ch - 'a' + 'A'; // to uppercase + break; + case all_lowercase: + break; + } + if(!has_trailing_underline) + { + for(auto &keyword : keywords) { - for(char ch : word) + if(name == keyword) { - if(ch >= 'a' && ch <= 'z') - ch = ch - 'a' + 'A'; // to uppercase - os << ch; + has_trailing_underline = true; + break; } - os << '_'; } } - struct Guard_macro - { - friend Generated_output_stream &operator<<(Generated_output_stream &os, Guard_macro) - { - write_guard_macro(os); - return os; - } - }; - static constexpr Guard_macro guard_macro{}; + if(has_trailing_underline) + name += '_'; + return name; +} + +struct Spirv_and_parser_generator : public Generator +{ + struct State; + virtual void run(Generator_args &generator_args, + const ast::Top_level &top_level) const override; +}; + +using namespace util::string_view_literals; + +struct Spirv_and_parser_generator::State +{ +private: const ast::Top_level &top_level; - Generated_output_stream spirv_h; - Generated_output_stream spirv_cpp; - Generated_output_stream parser_h; - Generated_output_stream parser_cpp; + detail::Generated_output_stream spirv_h; + detail::Generated_output_stream spirv_cpp; + detail::Generated_output_stream parser_h; + detail::Generated_output_stream parser_cpp; + +public: State(const util::filesystem::path &output_directory, const ast::Top_level &top_level) : top_level(top_level), spirv_h(output_directory / "spirv.h"), @@ -555,7 +309,9 @@ struct Spirv_and_parser_generator::State parser_cpp(output_directory / "parser.cpp") { } - void run() + +private: + void write_file_comments() { constexpr auto automatically_generated_file_warning_comment = R"(/* This file is automatically generated by generate_spirv_parser. DO NOT MODIFY. */ @@ -564,34 +320,435 @@ struct Spirv_and_parser_generator::State spirv_cpp << automatically_generated_file_warning_comment << top_level.copyright; parser_h << automatically_generated_file_warning_comment << top_level.copyright; parser_cpp << automatically_generated_file_warning_comment << top_level.copyright; - spirv_h << R"(#ifndef )" << guard_macro << R"( + } + static void write_opening_inclusion_guard(detail::Generated_output_stream &os) + { + using detail::guard_macro; + os << R"(#ifndef )" << guard_macro << R"( #define )" << guard_macro - << R"( + << R"( + )"; - parser_h << R"(#ifndef )" << guard_macro << R"( -#define )" << guard_macro - << R"( + } + static void write_closing_inclusion_guard(detail::Generated_output_stream &os) + { + using detail::guard_macro; + os << R"( +#endif /* )" + << guard_macro << R"( */ )"; - spirv_cpp << R"(#include ")" << spirv_h.get_file_path().filename().string() << R"(" + } + void write_opening_inclusion_guards() + { + write_opening_inclusion_guard(spirv_h); + write_opening_inclusion_guard(parser_h); + } + void write_closing_inclusion_guards() + { + write_closing_inclusion_guard(spirv_h); + write_closing_inclusion_guard(parser_h); + } + static void write_local_include(detail::Generated_output_stream &os, util::string_view file) + { + os << R"(#include ")" << file << R"(" )"; - parser_h << R"( -#include ")" << spirv_h.get_file_path().filename().string() - << R"(" + } + static void write_system_include(detail::Generated_output_stream &os, util::string_view file) + { + os << R"(#include <)" << file << R"(> )"; - parser_cpp << R"(#include ")" << parser_h.get_file_path().filename().string() << R"(" + } + void write_includes() + { + write_local_include(spirv_cpp, spirv_h.get_file_path().filename().string()); + write_local_include(parser_h, spirv_h.get_file_path().filename().string()); + write_local_include(parser_cpp, parser_h.get_file_path().filename().string()); + write_system_include(spirv_h, "cstdint"); + write_local_include(spirv_h, "util/string_view.h"); + write_local_include(spirv_h, "util/enum.h"); + } + static void write_opening_namespaces(detail::Generated_output_stream &os) + { + os << R"( +namespace vulkan_cpu +{ +namespace spirv +{ )"; + } + void write_opening_namespaces() + { + write_opening_namespaces(spirv_h); + write_opening_namespaces(spirv_cpp); + write_opening_namespaces(parser_h); + write_opening_namespaces(parser_cpp); + } + static void write_closing_namespaces(detail::Generated_output_stream &os) + { + os << R"(} +} +)"; + } + void write_closing_namespaces() + { + write_closing_namespaces(spirv_h); + write_closing_namespaces(spirv_cpp); + write_closing_namespaces(parser_h); + write_closing_namespaces(parser_cpp); + } + +private: + static constexpr util::string_view op_enum_json_name = "Op"_sv; + static constexpr util::string_view extension_enum_json_name = "Extension"_sv; + static constexpr util::string_view capability_enum_json_name = "Capability"_sv; + struct Enumerant_descriptor + { + std::uint32_t value; + std::string cpp_name; + std::string json_name; + ast::Capabilities capabilities; + ast::Extensions extensions; + static std::string make_cpp_name(util::string_view json_enumeration_name, + util::string_view json_enumerant_name) + { + using detail::name_from_words_all_lowercase; + bool starts_with_enumeration_name = false; + if(json_enumerant_name.substr(0, json_enumeration_name.size()) == json_enumeration_name) + starts_with_enumeration_name = true; + bool json_name_should_have_prefix = json_enumeration_name == op_enum_json_name; + if(json_name_should_have_prefix) + { + if(json_enumerant_name.substr(0, json_enumeration_name.size()) + != json_enumeration_name) + return name_from_words_all_lowercase( + json_enumeration_name, json_enumeration_name, json_enumerant_name) + .to_string(); + if(json_enumerant_name.substr(json_enumeration_name.size(), + json_enumeration_name.size()) + == json_enumeration_name) + return name_from_words_all_lowercase(json_enumeration_name, json_enumerant_name) + .to_string(); + return name_from_words_all_lowercase(json_enumerant_name).to_string(); + } + if(json_enumerant_name.empty()) + throw Generate_error("json enumerant name can't be empty"); + return name_from_words_all_lowercase(json_enumerant_name).to_string(); + } + Enumerant_descriptor(std::uint32_t value, + util::string_view json_enumeration_name, + std::string json_name, + ast::Capabilities capabilities, + ast::Extensions extensions) + : value(value), + cpp_name(make_cpp_name(json_enumeration_name, json_name)), + json_name(std::move(json_name)), + capabilities(std::move(capabilities)), + extensions(std::move(extensions)) + { + } + }; + struct Enumeration_descriptor + { + bool is_bitwise; + std::string cpp_name; + std::string json_name; + std::list enumerants; + typedef std::unordered_map::const_iterator> + Json_name_to_enumerant_map; + Json_name_to_enumerant_map json_name_to_enumerant_map; + static Json_name_to_enumerant_map make_json_name_to_enumerant_map( + const std::list *enumerants) + { + Json_name_to_enumerant_map retval; + for(auto i = enumerants->begin(); i != enumerants->end(); ++i) + retval[i->json_name] = i; + return retval; + } + Enumeration_descriptor(bool is_bitwise, + std::string json_name, + std::list enumerants) + : is_bitwise(is_bitwise), + cpp_name(detail::name_from_words_initial_capital(json_name).to_string()), + json_name(std::move(json_name)), + enumerants(std::move(enumerants)), + json_name_to_enumerant_map(make_json_name_to_enumerant_map(&this->enumerants)) + { + } + }; + +private: + std::list enumerations_list; + std::unordered_map::const_iterator> + enumerations_map; + util::optional::const_iterator> capability_enumeration; + util::optional::const_iterator> extension_enumeration; + util::optional::const_iterator> op_enumeration; + std::unordered_map::const_iterator> + instruction_set_extension_op_enumeration_map; + +private: + std::list::const_iterator add_enumeration( + Enumeration_descriptor &&enumeration_descriptor) + { + auto name = enumeration_descriptor.json_name; + auto iter = + enumerations_list.insert(enumerations_list.end(), std::move(enumeration_descriptor)); + if(!std::get<1>(enumerations_map.emplace(name, iter))) + throw Generate_error("duplicate enumeration: " + name); + return iter; + } + void fill_enumerations_helper(std::set &extensions_set, + const ast::Operand_kinds::Operand_kind &ast_operand_kind) + { + auto *ast_enumerants = + util::get_if(&ast_operand_kind.value); + if(ast_enumerants) + { + std::list enumerants; + for(auto &ast_enumerant : ast_enumerants->enumerants) + { + enumerants.push_back(Enumerant_descriptor(ast_enumerant.value, + ast_operand_kind.kind, + ast_enumerant.enumerant, + ast_enumerant.capabilities, + ast_enumerant.extensions)); + for(auto &extension : ast_enumerant.extensions.extensions) + extensions_set.insert(extension); + } + auto iter = add_enumeration(Enumeration_descriptor( + ast_operand_kind.category == ast::Operand_kinds::Operand_kind::Category::bit_enum, + ast_operand_kind.kind, + std::move(enumerants))); + if(ast_operand_kind.kind == capability_enum_json_name) + { + if(capability_enumeration) + throw Generate_error("Too many " + std::string(capability_enum_json_name) + + " enums"); + capability_enumeration = iter; + } + } + } + void fill_enumerations() + { + std::set extensions_set; + for(auto &operand_kind : top_level.operand_kinds.operand_kinds) + fill_enumerations_helper(extensions_set, operand_kind); + std::list op_enumerants; + for(auto &instruction : top_level.instructions.instructions) + { + op_enumerants.push_back(Enumerant_descriptor(instruction.opcode, + op_enum_json_name, + instruction.opname, + instruction.capabilities, + instruction.extensions)); + for(auto &extension : instruction.extensions.extensions) + extensions_set.insert(extension); + } + auto op_iter = add_enumeration(Enumeration_descriptor( + false, static_cast(op_enum_json_name), std::move(op_enumerants))); + op_enumeration = op_iter; + for(auto &instruction_set : top_level.extension_instruction_sets) + { + std::string json_enumeration_name = + std::string(op_enum_json_name) + " " + instruction_set.import_name; + std::list enumerants; + for(auto &instruction : instruction_set.instructions.instructions) + { + enumerants.push_back(Enumerant_descriptor(instruction.opcode, + json_enumeration_name, + instruction.opname, + instruction.capabilities, + instruction.extensions)); + for(auto &extension : instruction.extensions.extensions) + extensions_set.insert(extension); + } + auto iter = add_enumeration( + Enumeration_descriptor(false, json_enumeration_name, std::move(enumerants))); + instruction_set_extension_op_enumeration_map.emplace(instruction_set.import_name, iter); + } + std::list extension_enumerants; + std::uint32_t extension_index = 0; + for(auto &extension : extensions_set) + extension_enumerants.push_back(Enumerant_descriptor( + extension_index++, extension_enum_json_name, extension, {}, {})); + auto extension_iter = add_enumeration( + Enumeration_descriptor(false, + static_cast(extension_enum_json_name), + std::move(extension_enumerants))); + extension_enumeration = extension_iter; + if(!capability_enumeration) + throw Generate_error("missing " + std::string(capability_enum_json_name) + " enum"); + } + void write_basic_types() + { spirv_h << R"( -#endif /* )" << guard_macro - << R"( */ +typedef std::uint32_t Word; +typedef Word Id; +)"; + } + void write_enum_declarations() + { + spirv_h << "\n"; + for(auto &enumeration : enumerations_list) + spirv_h << "enum class " << enumeration.cpp_name << " : Word;\n"; + } + void write_enum_definitions() + { + using detail::unsigned_integer; + for(auto &enumeration : enumerations_list) + { + spirv_h << R"( +enum class )" << enumeration.cpp_name + << R"( : Word +{ +@+)"; + for(auto &enumerant : enumeration.enumerants) + { + spirv_h << enumerant.cpp_name << " = "; + if(enumeration.is_bitwise) + spirv_h << "0x" << unsigned_integer(enumerant.value, 0x10); + else + spirv_h << unsigned_integer(enumerant.value, 10); + spirv_h << ",\n"; + } + spirv_h << R"(@-}; + +vulkan_cpu_util_generate_enum_traits()" + << enumeration.cpp_name; + for(auto &enumerant : enumeration.enumerants) + spirv_h << R"(, +`````````````````````````````````````)" + << enumeration.cpp_name << "::" << enumerant.cpp_name; + spirv_h << R"(); +)"; + } + } + const Enumerant_descriptor &get_capability(const std::string &capability) + { + auto &enumerant_map = capability_enumeration.value()->json_name_to_enumerant_map; + auto iter = enumerant_map.find(capability); + if(iter == enumerant_map.end()) + throw Generate_error("unknown capability: " + capability); + return *std::get<1>(*iter); + } + const Enumerant_descriptor &get_extension(const std::string &extension) + { + auto &enumerant_map = extension_enumeration.value()->json_name_to_enumerant_map; + auto iter = enumerant_map.find(extension); + if(iter == enumerant_map.end()) + throw Generate_error("unknown extension: " + extension); + return *std::get<1>(*iter); + } + void write_enum_properties_definitions() + { + for(auto &enumeration : enumerations_list) + { + spirv_h << R"( +constexpr util::string_view get_enumerant_name()" + << enumeration.cpp_name << R"( v) noexcept +{ + using namespace util::string_view_literals; + switch(v) + { +@+@+)"; + std::unordered_set values; + for(auto &enumerant : enumeration.enumerants) + { + if(std::get<1>(values.insert(enumerant.value))) + { + spirv_h << "case " << enumeration.cpp_name << "::" << enumerant.cpp_name << R"(: + return ")" << enumerant.json_name + << R"("_sv; )"; - parser_h << R"( -#endif /* )" << guard_macro - << R"( */ + } + } + spirv_h << R"(@-@_} + return ""_sv; +} + +constexpr util::Enum_set<)" + << capability_enumeration.value()->cpp_name + << R"(> get_directly_required_capabilities()" << enumeration.cpp_name + << R"( v) noexcept +{ + switch(v) + { +@+@+)"; + values.clear(); + for(auto &enumerant : enumeration.enumerants) + { + if(std::get<1>(values.insert(enumerant.value))) + { + spirv_h << "case " << enumeration.cpp_name << "::" << enumerant.cpp_name << R"(: + return {)"; + auto separator = ""_sv; + for(auto &capability : enumerant.capabilities.capabilities) + { + spirv_h << separator; + separator = ", "_sv; + spirv_h << capability_enumeration.value()->cpp_name + << "::" << get_capability(capability).cpp_name; + } + spirv_h << R"(}; )"; + } + } + spirv_h << R"(@-@_} + return {}; +} + +constexpr util::Enum_set<)" + << extension_enumeration.value()->cpp_name + << R"(> get_directly_required_extensions()" << enumeration.cpp_name + << R"( v) noexcept +{ + switch(v) + { +@+@+)"; + values.clear(); + for(auto &enumerant : enumeration.enumerants) + { + if(std::get<1>(values.insert(enumerant.value))) + { + spirv_h << "case " << enumeration.cpp_name << "::" << enumerant.cpp_name << R"(: + return {)"; + auto separator = ""_sv; + for(auto &extension : enumerant.extensions.extensions) + { + spirv_h << separator; + separator = ", "_sv; + spirv_h << extension_enumeration.value()->cpp_name + << "::" << get_extension(extension).cpp_name; + } + spirv_h << R"(}; +)"; + } + } + spirv_h << R"(@-@_} + return {}; +} +)"; + } + } + +public: + void run() + { + fill_enumerations(); + write_file_comments(); + write_opening_inclusion_guards(); #warning finish - spirv_h << R"( -#error generator not finished being implemented + spirv_h << R"(#error generator not finished being implemented + )"; + write_includes(); + write_opening_namespaces(); + write_basic_types(); + write_enum_declarations(); + write_enum_definitions(); + write_enum_properties_definitions(); + write_closing_namespaces(); + write_closing_inclusion_guards(); spirv_h.write_to_file(); spirv_cpp.write_to_file(); parser_h.write_to_file(); @@ -599,8 +756,9 @@ struct Spirv_and_parser_generator::State } }; -constexpr util::string_view - Spirv_and_parser_generator::State::Generated_output_stream::literal_command; +constexpr util::string_view Spirv_and_parser_generator::State::op_enum_json_name; +constexpr util::string_view Spirv_and_parser_generator::State::extension_enum_json_name; +constexpr util::string_view Spirv_and_parser_generator::State::capability_enum_json_name; void Spirv_and_parser_generator::run(Generator_args &generator_args, const ast::Top_level &top_level) const diff --git a/src/generate_spirv_parser/generate.h b/src/generate_spirv_parser/generate.h index 7e9ffdd..34aa3dc 100644 --- a/src/generate_spirv_parser/generate.h +++ b/src/generate_spirv_parser/generate.h @@ -25,17 +25,14 @@ #define GENERATE_SPIRV_PARSER_GENERATE_H_ #include "ast.h" +#include "util/filesystem.h" #include "util/string_view.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "word_iterator.h" #include +#include +#include +#include +#include namespace vulkan_cpu { @@ -48,6 +45,434 @@ struct Generate_error : public std::runtime_error using runtime_error::runtime_error; }; +namespace detail +{ +using namespace util::string_view_literals; + +constexpr util::string_view keywords[] = { + "alignas"_sv, + "alignof"_sv, + "and"_sv, + "and_eq"_sv, + "asm"_sv, + "atomic_cancel"_sv, + "atomic_commit"_sv, + "atomic_noexcept"_sv, + "auto"_sv, + "bitand"_sv, + "bitor"_sv, + "bool"_sv, + "break"_sv, + "case"_sv, + "catch"_sv, + "char"_sv, + "char16_t"_sv, + "char32_t"_sv, + "class"_sv, + "compl"_sv, + "concept"_sv, + "concepts"_sv, + "const"_sv, + "const_cast"_sv, + "constexpr"_sv, + "continue"_sv, + "decltype"_sv, + "default"_sv, + "delete"_sv, + "do"_sv, + "double"_sv, + "dynamic_cast"_sv, + "else"_sv, + "enum"_sv, + "explicit"_sv, + "export"_sv, + "extern"_sv, + "false"_sv, + "float"_sv, + "for"_sv, + "friend"_sv, + "goto"_sv, + "if"_sv, + "import"_sv, + "inline"_sv, + "int"_sv, + "long"_sv, + "module"_sv, + "modules"_sv, + "mutable"_sv, + "namespace"_sv, + "new"_sv, + "noexcept"_sv, + "not"_sv, + "not_eq"_sv, + "nullptr"_sv, + "operator"_sv, + "or"_sv, + "or_eq"_sv, + "private"_sv, + "protected"_sv, + "public"_sv, + "register"_sv, + "reinterpret_cast"_sv, + "requires"_sv, + "return"_sv, + "short"_sv, + "signed"_sv, + "sizeof"_sv, + "static"_sv, + "static_assert"_sv, + "static_cast"_sv, + "struct"_sv, + "switch"_sv, + "synchronized"_sv, + "template"_sv, + "this"_sv, + "thread_local"_sv, + "throw"_sv, + "true"_sv, + "try"_sv, + "typedef"_sv, + "typeid"_sv, + "typename"_sv, + "union"_sv, + "unsigned"_sv, + "using"_sv, + "virtual"_sv, + "void"_sv, + "volatile"_sv, + "wchar_t"_sv, + "while"_sv, + "xor"_sv, + "xor_eq"_sv, +}; + +class Generated_output_stream +{ +private: + std::deque value; + util::filesystem::path file_path; + +public: + explicit Generated_output_stream(util::filesystem::path file_path) noexcept + : value(), + file_path(std::move(file_path)) + { + } + const util::filesystem::path &get_file_path() const noexcept + { + return file_path; + } + +private: + static constexpr std::size_t output_tab_width_no_tabs_allowed = 0; + template + static void write_indent(Fn write_char, + std::size_t indent_depth, + std::size_t output_tab_width = output_tab_width_no_tabs_allowed) + { + if(output_tab_width != output_tab_width_no_tabs_allowed) + { + while(indent_depth >= output_tab_width) + { + indent_depth -= output_tab_width; + write_char('\t'); + } + } + while(indent_depth--) + write_char(' '); + } + static constexpr util::string_view literal_command = "literal:"_sv; + +public: + static constexpr char indent_indicator_char = ' '; + static constexpr char literal_indent_indicator_char = '`'; + static constexpr std::size_t indent_indicators_per_indent = 4; + static constexpr char escape_char = '@'; + static constexpr bool indent_blank_lines = false; + +public: + void write_to_file(bool do_reindent = true) const; + +private: + void write_unsigned_integer(std::uint64_t value, + unsigned base = 10, + std::size_t min_length = 1); + void write_signed_integer(std::int64_t value, unsigned base = 10); + void write_literal(util::string_view value) + { + *this << escape_char << literal_command << value.size() << escape_char << value + << escape_char; + } + +public: + template + Generated_output_stream &operator<<(T) = delete; + Generated_output_stream &operator<<(char ch) + { + value.push_back(ch); + return *this; + } + Generated_output_stream &operator<<(util::string_view sv) + { + for(char ch : sv) + *this << ch; + return *this; + } + Generated_output_stream &operator<<(const char *s) + { + return operator<<(util::string_view(s)); + } + Generated_output_stream &operator<<(const std::string &s) + { + return operator<<(util::string_view(s)); + } + Generated_output_stream &operator<<(std::uint64_t v) + { + write_unsigned_integer(v); + return *this; + } + Generated_output_stream &operator<<(std::int64_t v) + { + write_signed_integer(v); + return *this; + } + Generated_output_stream &operator<<(const ast::Copyright &v) + { + *this << "/*\n"; + for(auto &line : v.lines) + { + if(line.empty()) + { + *this << "`*\n"; + continue; + } + *this << "`* "; + bool was_last_star = false; + for(char ch : line) + { + if(was_last_star && ch == '/') + *this << ' '; + was_last_star = (ch == '*'); + *this << ch; + } + *this << "\n"; + } + *this << "`*/\n"; + return *this; + } + class Literal_holder + { + friend class Generated_output_stream; + + private: + util::string_view value; + constexpr explicit Literal_holder(util::string_view value) noexcept : value(value) + { + } + }; + Generated_output_stream &operator<<(const Literal_holder &v) + { + write_literal(v.value); + return *this; + } + static constexpr Literal_holder literal(util::string_view value) noexcept + { + return Literal_holder{value}; + } + class Unsigned_integer_holder + { + friend class Generated_output_stream; + + private: + std::uint64_t value; + unsigned base; + std::size_t min_length; + constexpr explicit Unsigned_integer_holder(std::uint64_t value, + unsigned base, + std::size_t min_length) noexcept + : value(value), + base(base), + min_length(min_length) + { + } + }; + Generated_output_stream &operator<<(const Unsigned_integer_holder &v) + { + write_unsigned_integer(v.value, v.base, v.min_length); + return *this; + } + static constexpr Unsigned_integer_holder unsigned_integer(std::uint64_t value, + unsigned base = 10, + std::size_t min_length = 1) noexcept + { + return Unsigned_integer_holder{value, base, min_length}; + } + class Signed_integer_holder + { + friend class Generated_output_stream; + + private: + std::int64_t value; + unsigned base; + constexpr explicit Signed_integer_holder(std::int64_t value, unsigned base) noexcept + : value(value), + base(base) + { + } + }; + Generated_output_stream &operator<<(const Signed_integer_holder &v) + { + write_signed_integer(v.value, v.base); + return *this; + } + static constexpr Signed_integer_holder signed_integer(std::int64_t value, + unsigned base = 10) noexcept + { + return Signed_integer_holder{value, base}; + } + struct Guard_macro + { + }; + Generated_output_stream &operator<<(Guard_macro); + enum Name_format + { + initial_capital, + all_lowercase, + all_uppercase, + all_uppercase_with_trailing_underline, + }; + +private: + static std::string name_from_words_helper(Name_format name_format, std::string name); + +public: + template + class Name_from_words_holder + { + friend class Generated_output_stream; + + private: + Name_format name_format; + Chained_word_iterator iter; + template + constexpr explicit Name_from_words_holder( + Name_format name_format, + Args &&... args) noexcept(noexcept(Chained_word_iterator(std:: + forward( + args)...))) + : name_format(name_format), iter(std::forward(args)...) + { + static_assert(sizeof...(Args) == N, ""); + } + + public: + std::string to_string() const + { + std::size_t name_size = 0; + for(const util::string_view &word : iter) + { + // don't skip first '_' to allow for trailing '_' to prevent generating keywords + name_size += 1 + word.size(); + } + std::string name; + name.reserve(name_size); + bool first = true; + for(const util::string_view &word : iter) + { + if(first) + first = false; + else + name += '_'; + name += word; + } + return name_from_words_helper(name_format, std::move(name)); + } + }; + template + Generated_output_stream &operator<<(const Name_from_words_holder &v) + { + *this << v.to_string(); + return *this; + } + template + static constexpr Name_from_words_holder + name_from_words(Name_format name_format, Args &&... args) noexcept( + noexcept(Name_from_words_holder(name_format, + std::forward(args)...))) + { + return Name_from_words_holder(name_format, + std::forward(args)...); + } +}; + +constexpr auto literal(util::string_view value) noexcept +{ + return Generated_output_stream::literal(value); +} + +constexpr auto unsigned_integer(std::uint64_t value) noexcept +{ + return Generated_output_stream::unsigned_integer(value); +} + +constexpr auto unsigned_integer(std::uint64_t value, unsigned base) noexcept +{ + return Generated_output_stream::unsigned_integer(value, base); +} + +constexpr auto unsigned_integer(std::uint64_t value, unsigned base, std::size_t min_length) noexcept +{ + return Generated_output_stream::unsigned_integer(value, base, min_length); +} + +constexpr auto signed_integer(std::int64_t value) noexcept +{ + return Generated_output_stream::signed_integer(value); +} + +constexpr auto signed_integer(std::int64_t value, unsigned base) noexcept +{ + return Generated_output_stream::signed_integer(value, base); +} + +constexpr Generated_output_stream::Guard_macro guard_macro{}; + +template +constexpr auto name_from_words(Generated_output_stream::Name_format name_format, Args &&... args) noexcept( + noexcept(Generated_output_stream::name_from_words(name_format, std::forward(args)...))) +{ + return Generated_output_stream::name_from_words(name_format, std::forward(args)...); +} + +template +constexpr auto name_from_words_all_lowercase(Args &&... args) noexcept( + noexcept(Generated_output_stream::name_from_words(Generated_output_stream::all_lowercase, std::forward(args)...))) +{ + return Generated_output_stream::name_from_words(Generated_output_stream::all_lowercase, std::forward(args)...); +} + +template +constexpr auto name_from_words_all_uppercase(Args &&... args) noexcept( + noexcept(Generated_output_stream::name_from_words(Generated_output_stream::all_uppercase, std::forward(args)...))) +{ + return Generated_output_stream::name_from_words(Generated_output_stream::all_uppercase, std::forward(args)...); +} + +template +constexpr auto name_from_words_initial_capital(Args &&... args) noexcept( + noexcept(Generated_output_stream::name_from_words(Generated_output_stream::initial_capital, std::forward(args)...))) +{ + return Generated_output_stream::name_from_words(Generated_output_stream::initial_capital, std::forward(args)...); +} + +template +constexpr auto name_from_words_all_uppercase_with_trailing_underline(Args &&... args) noexcept( + noexcept(Generated_output_stream::name_from_words(Generated_output_stream::all_uppercase_with_trailing_underline, std::forward(args)...))) +{ + return Generated_output_stream::name_from_words(Generated_output_stream::all_uppercase_with_trailing_underline, std::forward(args)...); +} +} + struct Generator { struct Generator_args diff --git a/src/generate_spirv_parser/parser.cpp b/src/generate_spirv_parser/parser.cpp index f48eb1e..3a928b4 100644 --- a/src/generate_spirv_parser/parser.cpp +++ b/src/generate_spirv_parser/parser.cpp @@ -750,7 +750,7 @@ ast::Extension_instruction_set parse_extension_instruction_set(json::ast::Value file_name.size() - file_name_suffix.size(), file_name_suffix.size(), file_name_suffix) != 0) throw Parse_error(top_level_value.location, {}, "file name is unrecognizable"); - auto instruction_set_name = std::move(file_name); + auto instruction_set_name = file_name; instruction_set_name.erase(instruction_set_name.size() - file_name_suffix.size(), file_name_suffix.size()); instruction_set_name.erase(0, file_name_prefix.size()); if(top_level_value.get_value_kind() != json::ast::Value_kind::object) @@ -786,9 +786,9 @@ ast::Extension_instruction_set parse_extension_instruction_set(json::ast::Value throw Parse_error(entry_value.location, path_builder.path(), "unknown key"); } } - return ast::Extension_instruction_set( + auto retval = ast::Extension_instruction_set( std::move(instruction_set_name), - std::move(import_name), + import_name, get_value_or_throw_parse_error( std::move(copyright), top_level_value.location, nullptr, "missing copyright"), get_value_or_throw_parse_error( @@ -797,6 +797,8 @@ ast::Extension_instruction_set parse_extension_instruction_set(json::ast::Value revision, top_level_value.location, nullptr, "missing revision"), get_value_or_throw_parse_error( std::move(instructions), top_level_value.location, nullptr, "missing instructions")); + std::cerr << "Parsed extension instruction set: " << import_name << " from " << file_name << std::endl; + return retval; } } diff --git a/src/generate_spirv_parser/word_iterator.h b/src/generate_spirv_parser/word_iterator.h new file mode 100644 index 0000000..0e98c0d --- /dev/null +++ b/src/generate_spirv_parser/word_iterator.h @@ -0,0 +1,268 @@ +/* + * Copyright 2017 Jacob Lifshay + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef GENERATE_SPIRV_PARSER_WORD_ITERATOR_H_ +#define GENERATE_SPIRV_PARSER_WORD_ITERATOR_H_ + +#include "util/string_view.h" +#include "util/optional.h" +#include + +namespace vulkan_cpu +{ +namespace generate_spirv_parser +{ +namespace generate +{ +class Word_iterator +{ +public: + typedef std::ptrdiff_t difference_type; + typedef util::string_view value_type; + typedef const util::string_view &reference; + typedef const util::string_view *pointer; + typedef std::input_iterator_tag iterator_category; + +private: + enum class Char_class + { + uppercase, + number, + other_identifier, + word_separator + }; + static constexpr Char_class get_char_class(char ch) noexcept + { + if(ch >= 'A' && ch <= 'Z') + return Char_class::uppercase; + if(ch >= 'a' && ch <= 'z') + return Char_class::other_identifier; + if(ch >= '0' && ch <= '9') + return Char_class::number; + return Char_class::word_separator; + } + +private: + util::string_view word; + util::string_view words; + +private: + constexpr void next() noexcept + { + util::optional word_start; + Char_class last_char_class = Char_class::word_separator; + for(std::size_t i = 0; i < words.size(); i++) + { + auto current_char_class = get_char_class(words[i]); + if(word_start) + { + switch(current_char_class) + { + case Char_class::word_separator: + word = util::string_view(words.data() + *word_start, i - *word_start); + words.remove_prefix(i); + return; + case Char_class::uppercase: + if(last_char_class != Char_class::uppercase + && last_char_class != Char_class::number) + { + word = util::string_view(words.data() + *word_start, i - *word_start); + words.remove_prefix(i); + return; + } + if(i + 1 < words.size() + && get_char_class(words[i + 1]) == Char_class::other_identifier) + { + word = util::string_view(words.data() + *word_start, i - *word_start); + words.remove_prefix(i); + return; + } + break; + case Char_class::other_identifier: + case Char_class::number: + break; + } + } + else if(current_char_class != Char_class::word_separator) + { + word_start = i; + } + last_char_class = current_char_class; + } + if(word_start) + word = util::string_view(words.data() + *word_start, words.size() - *word_start); + else + word = {}; + words = {}; + } + constexpr bool at_end() const noexcept + { + return word.empty(); + } + +public: + constexpr Word_iterator() noexcept : word(), words() + { + } + constexpr explicit Word_iterator(util::string_view words) noexcept : word(), words(words) + { + next(); + } + constexpr const util::string_view &operator*() const noexcept + { + return word; + } + constexpr const util::string_view *operator->() const noexcept + { + return &word; + } + constexpr Word_iterator &operator++() noexcept + { + next(); + return *this; + } + constexpr Word_iterator operator++(int) noexcept + { + auto retval = *this; + next(); + return retval; + } + constexpr bool operator==(const Word_iterator &rt) const noexcept + { + return word.empty() == rt.word.empty(); + } + constexpr bool operator!=(const Word_iterator &rt) const noexcept + { + return word.empty() != rt.word.empty(); + } + constexpr Word_iterator begin() const noexcept + { + return *this; + } + constexpr Word_iterator end() const noexcept + { + return {}; + } +}; + +template +class Chained_word_iterator +{ +public: + typedef std::ptrdiff_t difference_type; + typedef util::string_view value_type; + typedef const util::string_view &reference; + typedef const util::string_view *pointer; + typedef std::input_iterator_tag iterator_category; + +private: + Word_iterator iterators[Iterator_count]; + std::size_t current_iterator_index; + +private: + template + static constexpr bool variadic_and() noexcept + { + for(bool v : {Values...}) + if(!v) + return false; + return true; + } + static constexpr util::string_view to_string_view_helper(util::string_view v) noexcept + { + return v; + } + constexpr bool at_end() const noexcept + { + return current_iterator_index == Iterator_count; + } + constexpr void next() noexcept + { + assert(current_iterator_index < Iterator_count); + ++iterators[current_iterator_index]; + while(iterators[current_iterator_index] == Word_iterator()) + if(++current_iterator_index == Iterator_count) + return; + } + +public: + constexpr Chained_word_iterator() noexcept : iterators{}, current_iterator_index(Iterator_count) + { + } + template + constexpr explicit Chained_word_iterator(Args &&... args) noexcept( + variadic_and(args)))...>()) + : iterators{Word_iterator(to_string_view_helper(std::forward(args)))...}, + current_iterator_index(0) + { + } + constexpr const util::string_view &operator*() const noexcept + { + assert(current_iterator_index < Iterator_count); + return *iterators[current_iterator_index]; + } + constexpr const util::string_view *operator->() const noexcept + { + return &operator*(); + } + constexpr Chained_word_iterator &operator++() noexcept + { + next(); + return *this; + } + constexpr Chained_word_iterator operator++(int) noexcept + { + auto retval = *this; + next(); + return retval; + } + constexpr bool operator==(const Chained_word_iterator &rt) const noexcept + { + return at_end() == rt.at_end(); + } + constexpr bool operator!=(const Chained_word_iterator &rt) const noexcept + { + return at_end() != rt.at_end(); + } + constexpr Chained_word_iterator begin() const noexcept + { + return *this; + } + constexpr Chained_word_iterator end() const noexcept + { + return {}; + } +}; + +template +constexpr Chained_word_iterator + make_chained_word_iterator(Args &&... args) noexcept( + noexcept(Chained_word_iterator(std::forward(args)...))) +{ + return Chained_word_iterator(std::forward(args)...); +} +} +} +} + +#endif /* GENERATE_SPIRV_PARSER_WORD_ITERATOR_H_ */