From: Jacob Lifshay Date: Fri, 30 Jun 2017 03:22:36 +0000 (-0700) Subject: working on refactoring X-Git-Tag: gsoc-2017~73^2~19 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=3be7558a284d10d18c8869b345a7d3e51b411768;p=kazan.git working on refactoring --- diff --git a/src/generate_spirv_parser/generate-old.cpp b/src/generate_spirv_parser/generate-old.cpp new file mode 100644 index 0000000..ab78874 --- /dev/null +++ b/src/generate_spirv_parser/generate-old.cpp @@ -0,0 +1,2342 @@ +/* + * 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. + * + */ +#include "generate-old.h" +#include "json/json.h" +#include "util/optional.h" +#include +#include +#include +#include +#include + +namespace vulkan_cpu +{ +namespace generate_spirv_parser +{ +namespace generate +{ +Generator::Generator_state::Generator_state(const Generator *generator, + Generator_args &generator_args, + const ast::Top_level &top_level) + : generator_args(generator_args), + indent_level(0), + full_output_file_name(generator_args.output_directory + "/" + + generator->output_base_file_name), + guard_macro_name(get_guard_macro_name_from_file_name(full_output_file_name)), + os(), + top_level(top_level), + operand_kind_map(), + operand_has_any_parameters_map() +{ + os.exceptions(std::ios::badbit | std::ios::failbit); + for(auto &operand_kind : top_level.operand_kinds.operand_kinds) + { + operand_kind_map.emplace(operand_kind.kind, &operand_kind); + bool &has_any_parameters = operand_has_any_parameters_map[&operand_kind]; + has_any_parameters = false; + if(util::holds_alternative( + operand_kind.value)) + { + auto &enumerants = + util::get(operand_kind.value); + for(auto &enumerant : enumerants.enumerants) + { + if(!enumerant.parameters.empty()) + { + has_any_parameters = true; + break; + } + } + } + } +} + +void Generator::Generator_state::open_output_file() +{ + os.open(full_output_file_name); +} + +constexpr Generator::Indent_t Generator::indent; +constexpr const char *Generator::vulkan_cpu_namespace_name; +constexpr const char *Generator::spirv_namespace_name; +constexpr const char *Generator::spirv_namespace_names[]; +constexpr const char *Generator::extension_enum_name; +constexpr const char *Generator::capability_enum_name; +constexpr const char *Generator::op_enum_name; + +std::string Generator::get_guard_macro_name_from_file_name(std::string file_name) +{ + auto retval = std::move(file_name); + for(char &ch : retval) + { + if(ch >= 'a' && ch <= 'z') + { + ch = ch - 'a' + 'A'; // convert to uppercase + continue; + } + if(ch >= 'A' && ch <= 'Z') + continue; + if(ch >= '0' && ch <= '9') + continue; + ch = '_'; + } + retval += '_'; + if(retval[0] >= '0' && retval[0] <= '9') + retval.insert(0, 1, '_'); + for(std::size_t double_underline_index = retval.find("__"); + double_underline_index != std::string::npos; + double_underline_index = retval.find("__", double_underline_index + 1)) + { + // insert a u in all pairs of underlines to prevent generating a reserved identifier + retval.insert(++double_underline_index, "u"); + } + if(retval.size() >= 2 && retval[0] == '_' && retval[1] >= 'A' && retval[1] <= 'Z') + { + // insert a u to prevent generating a reserved identifier: starting with an underline and a + // capital letter + retval.insert(1, "u"); + } + return retval; +} + +namespace +{ +constexpr bool is_uppercase_letter(char ch) noexcept +{ + if(ch >= 'A' && ch <= 'Z') + return true; + return false; +} + +constexpr bool is_lowercase_letter(char ch) noexcept +{ + if(ch >= 'a' && ch <= 'z') + return true; + return false; +} + +constexpr bool is_letter(char ch) noexcept +{ + return is_uppercase_letter(ch) || is_lowercase_letter(ch); +} + +constexpr bool is_identifier_start(char ch) noexcept +{ + return is_letter(ch) || ch == '_'; +} + +constexpr bool is_digit(char ch) noexcept +{ + if(ch >= '0' && ch <= '9') + return true; + return false; +} +} + +std::string Generator::get_enumerant_name(const char *enumeration_name, + std::size_t enumeration_name_size, + std::string enumerant_name, + bool input_name_should_have_prefix) +{ + bool starts_with_enumeration_name = + enumerant_name.compare(0, enumeration_name_size, enumeration_name, enumeration_name_size) + == 0; + bool starts_with_doubled_enumeration_name = false; + if(starts_with_enumeration_name) + starts_with_doubled_enumeration_name = enumerant_name.compare(enumeration_name_size, + enumeration_name_size, + enumeration_name, + enumeration_name_size) + == 0; + std::size_t needed_prefix_count; + if(input_name_should_have_prefix) + { + if(!starts_with_enumeration_name) + needed_prefix_count = 2; + else if(starts_with_doubled_enumeration_name) + needed_prefix_count = 1; + else + needed_prefix_count = 0; + } + else + { + if(starts_with_enumeration_name) + needed_prefix_count = 1; // ensure that we don't end up with name collisions + else if(enumerant_name.empty()) + needed_prefix_count = 1; // return something other than the empty string + else + needed_prefix_count = is_identifier_start(enumerant_name[0]) ? 0 : 1; + } + for(std::size_t i = 0; i < needed_prefix_count; i++) + enumerant_name.insert(0, enumeration_name, enumeration_name_size); + return enumerant_name; +} + +void Generator::write_indent_absolute(Generator_state &state, std::size_t amount) +{ + static constexpr auto indent_string = " "; + for(std::size_t i = 0; i < amount; i++) + state << indent_string; +} + +void Generator::write_indent_interpreted_text(Generator_state &state, + const char *text, + std::ptrdiff_t offset, + bool start_indented) +{ + bool did_indent = start_indented; + std::size_t indent_amount = offset + state.indent_level; + for(; *text; text++) + { + auto &ch = *text; + if(ch == '\n') + { + state << ch; + did_indent = false; + indent_amount = offset + state.indent_level; + } + else if(!did_indent && ch == '`') + { + indent_amount++; + } + else + { + if(!did_indent) + { + did_indent = true; + write_indent_absolute(state, indent_amount); + } + state << ch; + } + } +} + +void Generator::write_automatically_generated_file_warning(Generator_state &state) +{ + state + << "/* This file is automatically generated by generate_spirv_parser. DO NOT MODIFY. */\n"; +} + +void Generator::write_copyright_comment(Generator_state &state, const ast::Copyright ©right) +{ + state << "/*\n"; + for(auto &line : copyright.lines) + { + if(line.empty()) + { + state << " *\n"; + continue; + } + state << " * "; + bool was_last_star = false; + for(char ch : line) + { + if(was_last_star && ch == '/') + state << ' '; + was_last_star = (ch == '*'); + state << ch; + } + state << "\n"; + } + state << " */\n"; +} + +void Generator::write_file_guard_start(Generator_state &state) +{ + state << "#ifndef " << state.guard_macro_name << R"( +#define )" + << state.guard_macro_name << "\n" + "\n"; +} + +void Generator::write_file_guard_end(Generator_state &state) +{ + state << "#endif /* " << state.guard_macro_name << " */\n"; +} + +void Generator::write_namespace_start(Generator_state &state, const char *namespace_name) +{ + state << "namespace " << namespace_name << "\n" + "{\n"; +} + +void Generator::write_namespace_start(Generator_state &state, const std::string &namespace_name) +{ + state << "namespace " << namespace_name << "\n" + "{\n"; +} + +void Generator::write_namespace_end(Generator_state &state) +{ + state << "}\n"; +} + +void Generator::write_unsigned_integer_literal(Generator_state &state, + std::uint64_t value, + Integer_literal_base base, + std::size_t minimum_digit_count) +{ + constexpr std::uint64_t max_unsigned_value = std::numeric_limits::max(); + constexpr std::uint64_t max_unsigned_long_value = std::numeric_limits::max(); + auto literal_type = + value <= max_unsigned_value ? "U" : value <= max_unsigned_long_value ? "UL" : "ULL"; + auto number_prefix = ""; + unsigned base_as_number = 10; + switch(base) + { + case Integer_literal_base::dec: + minimum_digit_count = 1; + break; + case Integer_literal_base::hex: + base_as_number = 0x10; + number_prefix = "0x"; + break; + case Integer_literal_base::oct: + base_as_number = 010; + number_prefix = "0"; + break; + } + auto number_string = json::ast::Number_value::append_unsigned_integer_to_string( + value, number_prefix, base_as_number, minimum_digit_count) + + literal_type; + state << number_string; +} + +void Generator::write_signed_integer_literal(Generator_state &state, std::int64_t value) +{ + constexpr std::int64_t max_int_value = std::numeric_limits::max(); + constexpr std::int64_t min_int_value = std::numeric_limits::min(); + constexpr std::int64_t max_long_value = std::numeric_limits::max(); + constexpr std::int64_t min_long_value = std::numeric_limits::min(); + auto literal_type = ""; + if(value < min_int_value || value > max_int_value) + literal_type = "L"; + if(value < min_long_value || value > max_long_value) + literal_type = "LL"; + state << value << literal_type; +} + +struct Generator::Get_extensions_visitor +{ + std::unordered_set &retval; + constexpr Get_extensions_visitor(std::unordered_set &retval) noexcept + : retval(retval) + { + } + template + void operator()(const T &) + { + } + void operator()(const ast::Extensions &extensions) + { + for(auto &extension : extensions.extensions) + retval.insert(extension); + } +}; + +std::unordered_set Generator::get_extensions(const ast::Top_level &top_level) +{ + std::unordered_set retval; + top_level.visit(Get_extensions_visitor(retval)); + return retval; +} + +void Generator::write_capabilities_set(Generator_state &state, + const ast::Capabilities &capabilities) +{ + state << "util::Enum_set<" << capability_enum_name << ">{"; + auto separator = ""; + for(auto &capability : capabilities.capabilities) + { + state << separator << capability_enum_name + << "::" << get_enumerant_name(capability_enum_name, capability, false); + separator = ", "; + } + state << "}"; +} + +void Generator::write_extensions_set(Generator_state &state, const ast::Extensions &extensions) +{ + state << "util::Enum_set<" << extension_enum_name << ">{"; + auto separator = ""; + for(auto &extension : extensions.extensions) + { + state << separator << extension_enum_name + << "::" << get_enumerant_name(extension_enum_name, extension, false); + separator = ", "; + } + state << "}"; +} + +std::string Generator::get_name_from_words(const std::string &words) +{ + enum class Char_class + { + Uppercase, + OtherIdentifier, + WordSeparator, + }; + auto get_char_class = [](char ch) -> Char_class + { + if(is_uppercase_letter(ch)) + return Char_class::Uppercase; + if(is_letter(ch) || is_digit(ch)) + return Char_class::OtherIdentifier; + return Char_class::WordSeparator; + }; + auto find_words = [&](auto found_word_callback) -> void + { + util::optional word_start; + auto finish_word = [&](std::size_t index) + { + found_word_callback(util::string_view(words.data() + *word_start, index - *word_start)); + word_start = {}; + }; + auto start_word = [&](std::size_t index) + { + word_start = index; + }; + auto last_char_class = Char_class::WordSeparator; + 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::WordSeparator: + finish_word(i); + break; + case Char_class::Uppercase: + if(last_char_class != Char_class::Uppercase) + { + finish_word(i); + start_word(i); + } + else if(i + 1 < words.size() + && get_char_class(words[i + 1]) == Char_class::OtherIdentifier) + { + finish_word(i); + start_word(i); + } + break; + case Char_class::OtherIdentifier: + break; + } + } + else if(current_char_class != Char_class::WordSeparator) + { + start_word(i); + } + last_char_class = current_char_class; + } + if(word_start) + finish_word(words.size()); + }; + std::size_t retval_size = 0; + bool first = true; + find_words([&](util::string_view word) + { + if(!first) + retval_size++; // separating '_' + first = false; + retval_size += word.size(); + }); + std::string retval; + retval.reserve(retval_size); + first = true; + find_words([&](util::string_view word) + { + if(!first) + retval += '_'; + first = false; + retval += word; + }); + for(char &ch : retval) + { + if(is_uppercase_letter(ch)) + ch = ch - 'A' + 'a'; // to lowercase + } + static constexpr const char *const reserved_words[] = { + "alignas", + "alignof", + "and", + "and_eq", + "asm", + "atomic_cancel", + "atomic_commit", + "atomic_noexcept", + "auto", + "bitand", + "bitor", + "bool", + "break", + "case", + "catch", + "char", + "char16_t", + "char32_t", + "class", + "compl", + "concept", + "concepts", + "const", + "const_cast", + "constexpr", + "continue", + "decltype", + "default", + "delete", + "do", + "double", + "dynamic_cast", + "else", + "enum", + "explicit", + "export", + "extern", + "false", + "float", + "for", + "friend", + "goto", + "if", + "import", + "inline", + "int", + "long", + "module", + "modules", + "mutable", + "namespace", + "new", + "noexcept", + "not", + "not_eq", + "nullptr", + "operator", + "or", + "or_eq", + "private", + "protected", + "public", + "register", + "reinterpret_cast", + "requires", + "return", + "short", + "signed", + "sizeof", + "static", + "static_assert", + "static_cast", + "struct", + "switch", + "synchronized", + "template", + "this", + "thread_local", + "throw", + "true", + "try", + "typedef", + "typeid", + "typename", + "union", + "unsigned", + "using", + "virtual", + "void", + "volatile", + "wchar_t", + "while", + "xor", + "xor_eq", + }; + for(const char *reserved_word : reserved_words) + { + if(retval == reserved_word) + { + retval += '_'; + break; + } + } + return retval; +} + +#if 0 +#warning testing Generator::get_name_from_words +struct Generator::Tester +{ + struct Test_runner + { + Test_runner() + { + test(); + std::exit(1); + } + }; + static Test_runner test_runner; + static void test() + { + for(auto &input : { + "abc def", "AbcDef", "ABCDef", "'abc, def'", + }) + { + std::cout << "\"" << input << "\" -> " << get_name_from_words(input) + << std::endl; + } + } +}; + +Generator::Tester::Test_runner Generator::Tester::test_runner; +#endif + +std::string Generator::get_member_name_from_operand( + const ast::Instructions::Instruction::Operands::Operand &operand) +{ + if(!operand.name.empty()) + return get_name_from_words(operand.name); + util::string_view id_str = "Id"; + if(util::string_view(operand.kind).compare(0, id_str.size(), id_str) == 0 + && id_str.size() < operand.kind.size() + && is_uppercase_letter(operand.kind[id_str.size()])) + return get_name_from_words(operand.kind.substr(id_str.size())); + return get_name_from_words(operand.kind); +} + +std::string Generator::get_member_name_from_parameter( + const ast::Operand_kinds::Operand_kind::Enumerants::Enumerant::Parameters::Parameter ¶meter) +{ + if(!parameter.name.empty()) + return get_name_from_words(parameter.name); + return get_name_from_words(parameter.kind); +} + +std::string Generator::get_member_name_from_enumerant( + const ast::Operand_kinds::Operand_kind::Enumerants::Enumerant &enumerant) +{ + return get_name_from_words(enumerant.enumerant + " parameters"); +} + +std::string Generator::get_operand_with_parameters_name( + Generator_state &state, const ast::Operand_kinds::Operand_kind &operand_kind) +{ + if(get_operand_has_any_parameters(state, operand_kind)) + return operand_kind.kind + "_with_parameters"; + return operand_kind.kind; +} + +void Generator::write_struct_nonstatic_members_and_constructors(Generator_state &state, + const std::string &struct_name, + const std::string *member_types, + const std::string *member_names, + std::size_t member_count) +{ + for(std::size_t i = 0; i < member_count; i++) + state << indent << member_types[i] << " " << member_names[i] << ";\n"; + state << indent << struct_name << "()\n"; + { + auto push_indent = state.pushed_indent(); + for(std::size_t i = 0; i < member_count; i++) + { + state << indent; + if(i == 0) + state << ": "; + else + state << " "; + state << member_names[i] << "()"; + if(i != member_count - 1) + state << ","; + state << "\n"; + } + } + state << indent(R"({ +} +)"); + if(member_count != 0) + { + state << indent; + if(member_count == 1) + state << "explicit "; + state << struct_name << "("; + for(std::size_t i = 0; i < member_count; i++) + { + state << member_types[i] << " " << member_names[i]; + if(i != member_count - 1) + state << ", "; + } + state << ")\n"; + { + auto push_indent = state.pushed_indent(); + for(std::size_t i = 0; i < member_count; i++) + { + state << indent; + if(i == 0) + state << ": "; + else + state << " "; + state << member_names[i] << "(std::move(" << member_names[i] << "))"; + if(i != member_count - 1) + state << ","; + state << "\n"; + } + } + state << indent(R"({ +} +)"); + } +} + +std::vector + Generator::get_unique_enumerants( + std::vector enumerants) +{ + std::unordered_set values; + std::size_t output_index = 0; + for(std::size_t input_index = 0; input_index < enumerants.size(); input_index++) + { + if(std::get<1>(values.insert(enumerants[input_index].value))) + { + if(output_index != input_index) + enumerants[output_index] = std::move(enumerants[input_index]); + output_index++; + } + } + enumerants.erase(enumerants.begin() + output_index, enumerants.end()); + return enumerants; +} + +struct Spirv_header_generator final : public Generator +{ + Spirv_header_generator() : Generator("spirv.h") + { + } + enum class Enum_priority + { + default_priority = 0, + capability = 1, + }; + static Enum_priority get_enum_priority(const std::string &enum_name) noexcept + { + if(enum_name == capability_enum_name) + return Enum_priority::capability; + return Enum_priority::default_priority; + } + static bool compare_enum_names(const std::string &l, const std::string &r) noexcept + { + auto l_priority = get_enum_priority(l); + auto r_priority = get_enum_priority(r); + if(l_priority > r_priority) + return true; // higher priority sorts first + if(l_priority < r_priority) + return false; + return l < r; + } + /** lower priority means that the operand kind is declared first */ + static int get_operand_category_priority( + ast::Operand_kinds::Operand_kind::Category category) noexcept + { + switch(category) + { + case ast::Operand_kinds::Operand_kind::Category::bit_enum: + case ast::Operand_kinds::Operand_kind::Category::value_enum: + return 1; + case ast::Operand_kinds::Operand_kind::Category::id: + return 0; + case ast::Operand_kinds::Operand_kind::Category::literal: + return 0; + case ast::Operand_kinds::Operand_kind::Category::composite: + return 2; + } + return 0; + } + static bool compare_operand_kinds(const ast::Operand_kinds::Operand_kind &l, + const ast::Operand_kinds::Operand_kind &r) + { + auto l_priority = get_operand_category_priority(l.category); + auto r_priority = get_operand_category_priority(r.category); + if(l_priority != r_priority) + return l_priority < r_priority; + return compare_enum_names(l.kind, r.kind); + } + virtual void run(Generator_args &generator_args, const ast::Top_level &top_level) const override + { + Generator_state state(this, generator_args, top_level); + state.open_output_file(); + write_file_comments(state, top_level.copyright); + write_file_guard_start(state); + state << indent(R"(#include +#include "util/enum.h" +#include "util/optional.h" +#include "util/variant.h" +#include + +)"); + write_namespaces_start(state, spirv_namespace_names); + state << indent(R"(typedef std::uint32_t Word; +typedef Word Id; +enum class Op : Word; +constexpr Word magic_number = )") + << unsigned_hex_integer_literal(top_level.magic_number, 8) + << indent(true, + ";\n" + "constexpr std::uint32_t major_version = ") + << unsigned_dec_integer_literal(top_level.major_version) + << indent(true, + ";\n" + "constexpr std::uint32_t minor_version = ") + << unsigned_dec_integer_literal(top_level.minor_version) + << indent(true, + ";\n" + "constexpr std::uint32_t revision = ") + << unsigned_dec_integer_literal(top_level.revision) << ";\n"; + auto extensions_set = get_extensions(top_level); + std::vector extensions_list; + extensions_list.reserve(extensions_set.size()); + for(auto &extension : extensions_set) + extensions_list.push_back(extension); + std::sort(extensions_list.begin(), extensions_list.end()); + state << indent( + "\n" + "enum class ") + << extension_enum_name << indent(true, + " : std::size_t\n" + "{\n"); + { + auto push_indent = state.pushed_indent(); + for(auto &extension : extensions_list) + state << indent << get_enumerant_name(extension_enum_name, extension, false) + << ",\n"; + } + state << indent( + "};\n" + "\n" + "vulkan_cpu_util_generate_enum_traits(") + << extension_enum_name; + { + auto push_indent = state.pushed_indent(); + for(auto &extension : extensions_list) + state << ",\n" << indent << extension_enum_name + << "::" << get_enumerant_name(extension_enum_name, extension, false); + state << ");\n"; + } + state << indent( + "\n" + "constexpr const char *get_enumerant_name(") + << extension_enum_name << indent(true, + " v) noexcept\n" + "{\n"); + { + auto push_indent = state.pushed_indent(); + state << indent( + "switch(v)\n" + "{\n"); + for(auto &extension : extensions_list) + { + state << indent("case ") << extension_enum_name + << "::" << get_enumerant_name(extension_enum_name, extension, false) + << indent(true, + ":\n" + "`return \"") + << extension << "\";\n"; + } + state << indent( + "}\n" + "return \"\";\n"); + } + state << "}\n"; + std::vector operand_kinds; + operand_kinds.reserve(top_level.operand_kinds.operand_kinds.size()); + for(auto &operand_kind : top_level.operand_kinds.operand_kinds) + operand_kinds.push_back(&operand_kind); + std::sort( + operand_kinds.begin(), + operand_kinds.end(), + [](const ast::Operand_kinds::Operand_kind *a, const ast::Operand_kinds::Operand_kind *b) + { + return compare_operand_kinds(*a, *b); + }); + for(auto *operand_kind : operand_kinds) + { + switch(operand_kind->category) + { + case ast::Operand_kinds::Operand_kind::Category::bit_enum: + case ast::Operand_kinds::Operand_kind::Category::value_enum: + { + bool is_bit_enum = + operand_kind->category == ast::Operand_kinds::Operand_kind::Category::bit_enum; + auto &enumerants = + util::get(operand_kind->value); + auto unique_enumerants = get_unique_enumerants(enumerants.enumerants); + state << "\n" + "enum class " + << get_enum_name(*operand_kind) << " : Word\n" + "{\n"; + { + auto push_indent = state.pushed_indent(); + for(auto &enumerant : enumerants.enumerants) + { + state << indent + << get_enumerant_name(operand_kind->kind, enumerant.enumerant, false) + << " = "; + if(is_bit_enum) + state << unsigned_hex_integer_literal(enumerant.value); + else + state << unsigned_dec_integer_literal(enumerant.value); + state << ",\n"; + } + } + state << "};\n" + "\n" + "vulkan_cpu_util_generate_enum_traits(" + << get_enum_name(*operand_kind); + { + auto push_indent = state.pushed_indent(); + for(auto &enumerant : unique_enumerants) + state << ",\n" << indent << get_enum_name(*operand_kind) << "::" + << get_enumerant_name(operand_kind->kind, enumerant.enumerant, false); + state << ");\n"; + } + state << "\n" + "constexpr const char *get_enumerant_name(" + << get_enum_name(*operand_kind) << " v) noexcept\n" + "{\n"; + { + auto push_indent = state.pushed_indent(); + state << indent( + "switch(v)\n" + "{\n"); + for(auto &enumerant : unique_enumerants) + { + state << indent("case ") << get_enum_name(*operand_kind) << "::" + << get_enumerant_name(operand_kind->kind, enumerant.enumerant, false) + << indent(true, + ":\n" + "`return \"") + << enumerant.enumerant << "\";\n"; + } + state << indent( + "}\n" + "return \"\";\n"); + } + state << "}\n"; + state << "\n" + "constexpr util::Enum_set<" + << capability_enum_name << "> get_directly_required_capability_set(" + << get_enum_name(*operand_kind) << " v) noexcept\n" + "{\n"; + { + auto push_indent = state.pushed_indent(); + state << indent( + "switch(v)\n" + "{\n"); + for(auto &enumerant : unique_enumerants) + { + state << indent("case ") << get_enum_name(*operand_kind) << "::" + << get_enumerant_name(operand_kind->kind, enumerant.enumerant, false) + << indent(true, + ":\n" + "`return ") + << enumerant.capabilities << ";\n"; + } + state << indent( + "}\n" + "return {};\n"); + } + state << "}\n" + "\n" + "constexpr util::Enum_set<" + << extension_enum_name << "> get_directly_required_extension_set(" + << get_enum_name(*operand_kind) << " v) noexcept\n" + "{\n"; + { + auto push_indent = state.pushed_indent(); + state << indent( + "switch(v)\n" + "{\n"); + for(auto &enumerant : unique_enumerants) + { + state << indent("case ") << get_enum_name(*operand_kind) << "::" + << get_enumerant_name(operand_kind->kind, enumerant.enumerant, false) + << indent(true, + ":\n" + "`return ") + << enumerant.extensions << ";\n"; + } + state << indent( + "}\n" + "return {};\n"); + } + state << "}\n"; + break; + } + case ast::Operand_kinds::Operand_kind::Category::composite: + { + auto &bases = + util::get(operand_kind->value); + state << "\n" + "struct " + << operand_kind->kind << "\n" + "{\n"; + auto push_indent = state.pushed_indent(); + std::vector member_types; + std::vector member_names; + member_types.reserve(bases.values.size()); + member_names.reserve(bases.values.size()); + for(std::size_t i = 0; i < bases.values.size(); i++) + { + member_types.push_back( + get_operand_with_parameters_name(state, bases.values[i])); + member_names.push_back( + json::ast::Number_value::append_unsigned_integer_to_string(i + 1, "part_")); + } + write_struct_nonstatic_members_and_constructors(state, + operand_kind->kind, + member_types.data(), + member_names.data(), + bases.values.size()); + push_indent.finish(); + state << "};\n"; + break; + } + case ast::Operand_kinds::Operand_kind::Category::id: + { + auto &doc = util::get(operand_kind->value); + state << "\n" + "/** "; + bool was_last_star = false; + for(char ch : doc.value) + { + if(was_last_star && ch == '/') + state << ' '; + was_last_star = (ch == '*'); + state << ch; + } + state << " */\n" + "typedef Id " + << operand_kind->kind << ";\n"; + break; + } + case ast::Operand_kinds::Operand_kind::Category::literal: + { + auto &doc = util::get(operand_kind->value); + auto literal_kind = + ast::Operand_kinds::Operand_kind::get_literal_kind_from_json_name( + operand_kind->kind); + if(!literal_kind) + throw Generate_error("bad literal kind"); + auto base_type = "std::vector"; + switch(*literal_kind) + { + case ast::Operand_kinds::Operand_kind::Literal_kind::literal_integer: + // TODO: fix after determining if LiteralInteger can be multiple words + base_type = "std::uint32_t"; + break; + case ast::Operand_kinds::Operand_kind::Literal_kind::literal_string: + base_type = "std::string"; + break; + case ast::Operand_kinds::Operand_kind::Literal_kind:: + literal_context_dependent_number: + break; + case ast::Operand_kinds::Operand_kind::Literal_kind::literal_ext_inst_integer: + base_type = "Word"; + break; + case ast::Operand_kinds::Operand_kind::Literal_kind:: + literal_spec_constant_op_integer: + base_type = "Op"; + break; + } + state << "\n" + "/** "; + bool was_last_star = false; + for(char ch : doc.value) + { + if(was_last_star && ch == '/') + state << ' '; + was_last_star = (ch == '*'); + state << ch; + } + state << " */\n" + "typedef " + << base_type << " " << operand_kind->kind << ";\n"; + break; + } + } + } + for(auto *operand_kind : operand_kinds) + { + switch(operand_kind->category) + { + case ast::Operand_kinds::Operand_kind::Category::bit_enum: + case ast::Operand_kinds::Operand_kind::Category::value_enum: + { + bool is_bit_enum = + operand_kind->category == ast::Operand_kinds::Operand_kind::Category::bit_enum; + auto &enumerants = + util::get(operand_kind->value); + auto unique_enumerants = get_unique_enumerants(enumerants.enumerants); + if(get_operand_has_any_parameters(state, *operand_kind)) + { + for(auto &enumerant : unique_enumerants) + { + if(enumerant.parameters.empty()) + continue; + auto struct_name = get_enumerant_parameters_struct_name( + operand_kind->kind, enumerant.enumerant, false); + state << "\n" + "struct " + << struct_name << indent(true, R"( +{ +`static constexpr )") << get_enum_name(*operand_kind) + << indent(true, R"( get_enumerant() noexcept +`{ +``return )") << get_enum_name(*operand_kind) + << "::" + << get_enumerant_name(operand_kind->kind, enumerant.enumerant, false) + << indent(true, R"(; +`} +)"); + std::vector member_types; + std::vector member_names; + member_types.reserve(enumerant.parameters.parameters.size()); + member_names.reserve(enumerant.parameters.parameters.size()); + for(auto ¶meter : enumerant.parameters.parameters) + { + member_types.push_back( + get_operand_with_parameters_name(state, parameter.kind)); + member_names.push_back(get_member_name_from_parameter(parameter)); + } + auto push_indent = state.pushed_indent(); + write_struct_nonstatic_members_and_constructors( + state, + struct_name, + member_types.data(), + member_names.data(), + enumerant.parameters.parameters.size()); + push_indent.finish(); + state << "};\n"; + } + if(is_bit_enum) + { + auto struct_name = get_operand_with_parameters_name(state, *operand_kind); + state << indent(R"( +struct )") << struct_name << indent(true, R"( +{ +)"); + std::vector member_types; + std::vector member_names; + member_types.push_back(get_enum_name(*operand_kind)); + member_names.push_back("value"); + for(auto &enumerant : unique_enumerants) + { + if(enumerant.parameters.empty()) + continue; + member_types.push_back( + "util::optional<" + + get_enumerant_parameters_struct_name( + operand_kind->kind, enumerant.enumerant, false) + + ">"); + member_names.push_back(get_member_name_from_enumerant(enumerant)); + } + auto push_indent = state.pushed_indent(); + write_struct_nonstatic_members_and_constructors(state, + struct_name, + member_types.data(), + member_names.data(), + member_types.size()); + push_indent.finish(); + state << "};\n"; + } + else + { + auto struct_name = get_operand_with_parameters_name(state, *operand_kind); + state << indent(R"( +struct )") << struct_name << indent(true, R"( +{ +`typedef util::variantkind, enumerant.enumerant, false); + } + state << indent(true, R"(> Parameters; +)"); + auto push_indent = state.pushed_indent(); + constexpr std::size_t member_count = 2; + std::string member_types[member_count] = { + get_enum_name(*operand_kind), "Parameters", + }; + std::string member_names[member_count] = { + "value", "parameters", + }; + write_struct_nonstatic_members_and_constructors( + state, struct_name, member_types, member_names, member_count); + push_indent.finish(); + state << "};\n"; + } + } + break; + } + case ast::Operand_kinds::Operand_kind::Category::composite: + case ast::Operand_kinds::Operand_kind::Category::id: + case ast::Operand_kinds::Operand_kind::Category::literal: + break; + } + } + std::vector instructions; + instructions.reserve(top_level.instructions.instructions.size()); + for(auto &instruction : top_level.instructions.instructions) + instructions.push_back(&instruction); + std::sort( + instructions.begin(), + instructions.end(), + [](const ast::Instructions::Instruction *a, const ast::Instructions::Instruction *b) + { + return a->opcode < b->opcode; + }); + state << "\n" + "enum class " + << op_enum_name << " : Word\n" + "{\n"; + { + auto push_indent = state.pushed_indent(); + for(auto &instruction : top_level.instructions.instructions) + { + state << indent << get_enumerant_name(op_enum_name, instruction.opname, true) + << " = " << unsigned_dec_integer_literal(instruction.opcode) << ",\n"; + } + } + state << "};\n"; + state << "\n" + "vulkan_cpu_util_generate_enum_traits(" + << op_enum_name; + { + auto push_indent = state.pushed_indent(); + for(auto &instruction : top_level.instructions.instructions) + state << ",\n" << indent << op_enum_name + << "::" << get_enumerant_name(op_enum_name, instruction.opname, true); + state << ");\n"; + } + state << "\n" + "constexpr const char *get_enumerant_name(" + << op_enum_name << " v) noexcept\n" + "{\n"; + { + auto push_indent = state.pushed_indent(); + state << indent( + "switch(v)\n" + "{\n"); + for(auto &instruction : top_level.instructions.instructions) + { + state << indent("case ") << op_enum_name + << "::" << get_enumerant_name(op_enum_name, instruction.opname, true) + << indent(true, + ":\n" + "return \"") + << instruction.opname << "\";\n"; + } + state << indent( + "}\n" + "return \"\";\n"); + } + state << "}\n" + "\n" + "constexpr util::Enum_set<" + << capability_enum_name << "> get_directly_required_capability_set(" << op_enum_name + << " v) noexcept\n" + "{\n"; + { + auto push_indent = state.pushed_indent(); + state << indent( + "switch(v)\n" + "{\n"); + for(auto &instruction : top_level.instructions.instructions) + { + state << indent("case ") << op_enum_name + << "::" << get_enumerant_name(op_enum_name, instruction.opname, true) + << indent(true, + ":\n" + "return ") + << instruction.capabilities << ";\n"; + } + state << indent( + "}\n" + "return {};\n"); + } + state << "}\n" + "\n" + "constexpr util::Enum_set<" + << extension_enum_name << "> get_directly_required_extension_set(" << op_enum_name + << " v) noexcept\n" + "{\n"; + { + auto push_indent = state.pushed_indent(); + state << indent( + "switch(v)\n" + "{\n"); + for(auto &instruction : top_level.instructions.instructions) + { + state << indent("case ") << op_enum_name + << "::" << get_enumerant_name(op_enum_name, instruction.opname, true) + << ":\n"; + auto push_indent2 = state.pushed_indent(); + state << indent("return ") << instruction.extensions << ";\n"; + } + state << indent( + "}\n" + "return {};\n"); + } + state << "}\n"; + for(auto &instruction : top_level.instructions.instructions) + { + auto struct_name = get_enumerant_name(op_enum_name, instruction.opname, true); + state << "\n" + "struct " + << struct_name << "\n" + "{\n"; + { + auto push_indent = state.pushed_indent(); + state << indent("static constexpr ") << op_enum_name << " get_opcode() noexcept\n" + << indent("{\n"); + { + auto push_indent2 = state.pushed_indent(); + state << indent("return ") << op_enum_name + << "::" << get_enumerant_name(op_enum_name, instruction.opname, true) + << ";\n"; + } + state << indent("}\n"); + std::vector member_names; + std::vector member_types; + member_names.reserve(instruction.operands.operands.size()); + member_types.reserve(instruction.operands.operands.size()); + for(auto &operand : instruction.operands.operands) + { + std::string member_type; + switch(operand.quantifier) + { + case ast::Instructions::Instruction::Operands::Operand::Quantifier::none: + { + member_type = get_operand_with_parameters_name(state, operand); + break; + } + case ast::Instructions::Instruction::Operands::Operand::Quantifier::optional: + { + member_type = "util::optional<" + + get_operand_with_parameters_name(state, operand) + ">"; + break; + } + case ast::Instructions::Instruction::Operands::Operand::Quantifier::variable: + { + member_type = + "std::vector<" + get_operand_with_parameters_name(state, operand) + ">"; + break; + } + } + member_types.push_back(std::move(member_type)); + member_names.push_back(get_member_name_from_operand(operand)); + } + write_struct_nonstatic_members_and_constructors(state, + struct_name, + member_types.data(), + member_names.data(), + member_types.size()); + } + state << "};\n"; + } + write_namespaces_end(state, spirv_namespace_names); + write_file_guard_end(state); + } +}; + +struct Spirv_source_generator final : public Generator +{ + Spirv_source_generator() : Generator("spirv.cpp") + { + } + virtual void run(Generator_args &generator_args, const ast::Top_level &top_level) const override + { + Generator_state state(this, generator_args, top_level); + state.open_output_file(); + write_file_comments(state, top_level.copyright); + state << "#include \"spirv.h\"\n"; + } +}; + +struct Parser_header_generator final : public Generator +{ + Parser_header_generator() : Generator("parser.h") + { + } + static std::string get_dump_operand_function_name(std::string kind) + { + return "dump_operand_" + std::move(kind); + } + static std::string get_dump_operand_function_name( + const ast::Operand_kinds::Operand_kind &operand_kind) + { + return get_dump_operand_function_name(operand_kind.kind); + } + static std::string get_dump_operand_function_name( + const ast::Operand_kinds::Operand_kind::Enumerants::Enumerant::Parameters::Parameter + ¶meter) + { + return get_dump_operand_function_name(parameter.kind); + } + static std::string get_parse_operand_function_name(std::string kind) + { + return "parse_operand_" + std::move(kind); + } + static std::string get_parse_operand_function_name( + const ast::Operand_kinds::Operand_kind &operand_kind) + { + return get_parse_operand_function_name(operand_kind.kind); + } + static std::string get_parse_operand_function_name( + const ast::Instructions::Instruction::Operands::Operand &operand) + { + return get_parse_operand_function_name(operand.kind); + } + static std::string get_parse_operand_function_name( + const ast::Operand_kinds::Operand_kind::Enumerants::Enumerant::Parameters::Parameter + ¶meter) + { + return get_parse_operand_function_name(parameter.kind); + } + static std::string get_parse_instruction_function_name(std::string opname) + { + return "parse_instruction_" + get_enumerant_name(op_enum_name, opname, true); + } + virtual void run(Generator_args &generator_args, const ast::Top_level &top_level) const override + { + Generator_state state(this, generator_args, top_level); + state.open_output_file(); + write_file_comments(state, top_level.copyright); + write_file_guard_start(state); + state << R"(#include "spirv.h" +#include +#include +#include "util/optional.h" +#include "json/json.h" +#include + +)"; + write_namespaces_start(state, spirv_namespace_names); + state << indent(R"(struct Parse_error +{ +`std::size_t word_index; +`std::size_t instruction_word_index; +`std::string message; +`Parse_error(std::size_t word_index, std::size_t instruction_word_index, std::string message) noexcept +``: word_index(word_index), +`` instruction_word_index(instruction_word_index), +`` message(std::move(message)) +`{ +`} +`virtual ~Parse_error() = default; +}; + +)"); + state << "struct Parse_semantics_generic\n" + "{\n"; + { + auto push_indent = state.pushed_indent(); + state << indent(R"(virtual ~Parse_semantics_generic() = default; +virtual std::unique_ptr handle_error(std::size_t word_index, std::size_t instruction_word_index, std::string message) = 0; +virtual void handle_spirv_version(unsigned major, unsigned minor) = 0; +virtual void handle_generator_magic_number(Word value) = 0; +virtual void handle_id_bound(Word id_bound) = 0; +)"); + for(auto &instruction : top_level.instructions.instructions) + { + auto struct_name = get_enumerant_name(op_enum_name, instruction.opname, true); + state << indent("virtual void handle_instruction(") << struct_name + << " instruction) = 0;\n"; + } + } + state << indent(R"(}; + +struct Parse_dump final : public Parse_semantics_generic +{ +`std::ostream &os; +`explicit constexpr Parse_dump(std::ostream &os) noexcept : os(os) +`{ +`} +)"); + { + auto push_indent = state.pushed_indent(); + state << indent( + R"(virtual std::unique_ptr handle_error(std::size_t word_index, std::size_t instruction_word_index, std::string message) override; +virtual void handle_spirv_version(unsigned major, unsigned minor) override; +virtual void handle_generator_magic_number(Word value) override; +virtual void handle_id_bound(Word id_bound) override; +)"); + for(auto &instruction : top_level.instructions.instructions) + { + auto struct_name = get_enumerant_name(op_enum_name, instruction.opname, true); + state << indent("virtual void handle_instruction(") << struct_name + << " instruction) override;\n"; + } + for(auto &operand_kind : top_level.operand_kinds.operand_kinds) + { + auto dump_function_name = get_dump_operand_function_name(operand_kind); + state << indent("void ") << dump_function_name << "(const " + << get_operand_with_parameters_name(state, operand_kind) << " &value);\n"; + state << indent("void ") << dump_function_name << "(const util::optional<" + << get_operand_with_parameters_name(state, operand_kind) << "> &value);\n"; + state << indent("void ") << dump_function_name << "(const std::vector<" + << get_operand_with_parameters_name(state, operand_kind) << "> &values);\n"; + } + } + state << "};\n" + "\n" + "template \n" + "struct Parser\n" + "{\n"; + { + auto push_indent = state.pushed_indent(); + for(auto &operand_kind : top_level.operand_kinds.operand_kinds) + { + auto parse_function_name = get_parse_operand_function_name(operand_kind); + state + << indent(R"(static std::unique_ptr )") << parse_function_name + << indent( + true, + R"((const Word *words, std::size_t word_count, Semantics &semantics, std::size_t error_instruction_index, std::size_t &word_index, )") + << get_operand_with_parameters_name(state, operand_kind) << indent(true, + R"( &value) +{ +`if(word_index >= word_count) +``return semantics.handle_error(error_instruction_index + word_index, error_instruction_index, "instruction missing operand"); +)"); + auto push_indent = state.pushed_indent(); + switch(operand_kind.category) + { + case ast::Operand_kinds::Operand_kind::Category::bit_enum: + { + auto enum_name = get_enum_name(operand_kind); + if(get_operand_has_any_parameters(state, operand_kind)) + { + state << indent(R"(value.value = static_cast<)") << enum_name + << indent(true, R"(>(words[word_index++]); +)"); + auto &enumerants = util::get( + operand_kind.value); + for(auto &enumerant : enumerants.enumerants) + { + if(enumerant.parameters.empty()) + continue; + auto enumerant_member_name = get_member_name_from_enumerant(enumerant); + auto enumerant_name = + get_enumerant_name(operand_kind.kind, enumerant.enumerant, false); + state + << indent( + R"(if((static_cast(value.value) & static_cast()") + << enum_name << "::" << enumerant_name + << indent(true, R"()) == static_cast()") << enum_name + << "::" << enumerant_name << indent(true, R"()) +{ +`value.)") << enumerant_member_name + << indent(true, R"(.emplace(); +)"); + bool first = true; + for(auto ¶meter : enumerant.parameters.parameters) + { + auto parameter_member_name = + get_member_name_from_parameter(parameter); + auto parameter_parse_function = + get_parse_operand_function_name(parameter); + state << indent(1); + if(first) + { + state << indent(true, "auto "); + first = false; + } + state << indent(true, "parse_error = ") << parameter_parse_function + << "(words, word_count, semantics, error_instruction_index, " + "word_index, value." + << enumerant_member_name << "->" << parameter_member_name + << indent(true, R"(); +`if(parse_error) +``return parse_error; +)"); + } + state << indent(R"(} +)"); + } + } + else + { + state << indent(R"(value = static_cast<)") << enum_name + << indent(true, R"(>(words[word_index++]); +)"); + } + break; + } + case ast::Operand_kinds::Operand_kind::Category::value_enum: + { + auto enum_name = get_enum_name(operand_kind); + if(get_operand_has_any_parameters(state, operand_kind)) + { + state << indent(R"(value.value = static_cast<)") << enum_name + << indent(true, R"(>(words[word_index++]); +switch(value.value) +{ +)"); + auto &enumerants = util::get( + operand_kind.value); + for(auto &enumerant : enumerants.enumerants) + { + auto enumerant_name = + get_enumerant_name(operand_kind.kind, enumerant.enumerant, false); + state << indent("case ") << enum_name << "::" << enumerant_name + << indent(true, R"(: +)"); + if(enumerant.parameters.empty()) + { + state << indent(R"(`break; +)"); + continue; + } + auto enumerant_parameters_struct_name = + get_enumerant_parameters_struct_name( + operand_kind.kind, enumerant.enumerant, false); + state << indent(R"({ +`value.parameters.emplace<)") << enumerant_parameters_struct_name + << indent(true, R"(>(); +`auto ¶meters = util::get<)") << enumerant_parameters_struct_name + << indent(true, R"(>(value.parameters); +)"); + bool first = true; + for(auto ¶meter : enumerant.parameters.parameters) + { + auto parameter_member_name = + get_member_name_from_parameter(parameter); + auto parameter_parse_function = + get_parse_operand_function_name(parameter); + state << indent(1); + if(first) + { + state << indent(true, "auto "); + first = false; + } + state << indent(true, "parse_error = ") << parameter_parse_function + << "(words, word_count, semantics, error_instruction_index, " + "word_index, parameters." + << parameter_member_name << indent(true, R"(); +`if(parse_error) +``return parse_error; +)"); + } + state << indent(R"(`break; +} +)"); + } + state << indent(R"(default: +`word_index--; +`return semantics.handle_error(error_instruction_index + word_index, error_instruction_index, "invalid enum value"); +} +)"); + } + else + { + state << indent(R"(value = static_cast<)") << enum_name + << indent(true, R"(>(words[word_index]); +if(util::Enum_traits<)") << enum_name + << indent(true, R"(>::find_value(value) == util::Enum_traits<)") + << enum_name << indent(true, R"(>::npos) +`return semantics.handle_error(error_instruction_index + word_index, error_instruction_index, "invalid enum value"); +word_index++; +)"); + } + break; + } + case ast::Operand_kinds::Operand_kind::Category::composite: + { + auto &bases = + util::get(operand_kind.value); + for(std::size_t i = 0; i < bases.values.size(); i++) + { + state << indent; + if(i == 0) + state << indent(true, "auto "); + state << indent(true, "parse_error = ") + << get_parse_operand_function_name(bases.values[i]) + << "(words, word_count, semantics, error_instruction_index, " + "word_index, value." + << json::ast::Number_value::append_unsigned_integer_to_string(i + 1, + "part_") + << indent(true, R"(); +if(parse_error) +`return parse_error; +)"); + } + break; + } + case ast::Operand_kinds::Operand_kind::Category::id: + { + state << indent(R"(value = static_cast(words[word_index++]); +)"); + break; + } + case ast::Operand_kinds::Operand_kind::Category::literal: + { + auto literal_kind = + ast::Operand_kinds::Operand_kind::get_literal_kind_from_json_name( + operand_kind.kind); + if(!literal_kind) + throw Generate_error("bad literal kind"); + switch(*literal_kind) + { + case ast::Operand_kinds::Operand_kind::Literal_kind::literal_integer: + // TODO: fix after determining if LiteralInteger can be multiple words + state << indent(R"(value = words[word_index++]; +)"); + break; + case ast::Operand_kinds::Operand_kind::Literal_kind::literal_string: + state << indent( + R"(value.clear(); +bool done = false; +while(!done) +{ +`if(word_index >= word_count) +``return semantics.handle_error(error_instruction_index + word_index, error_instruction_index, "string missing terminating null"); +`Word word = words[word_index++]; +`for(std::size_t i = 0; i < 4; i++) +`{ +``unsigned char ch = word & 0xFFU; +``word >>= 8; +``if(ch == '\0') +``{ +```done = true; +```if(word != 0) +````return semantics.handle_error(error_instruction_index + word_index, error_instruction_index, "string has non-zero padding"); +``} +``else +``{ +```value += ch; +``} +`} +} +)"); + break; + case ast::Operand_kinds::Operand_kind::Literal_kind:: + literal_context_dependent_number: + state << indent( + R"(static_assert(std::is_same &>::value, "missing parse code for operand kind"); +value.clear(); +value.reserve(word_count - word_index); +while(word_index < word_count) +`value.push_back(words[word_index++]); +)"); + break; + case ast::Operand_kinds::Operand_kind::Literal_kind::literal_ext_inst_integer: + state << indent(R"(value = words[word_index++]; +)"); + break; + case ast::Operand_kinds::Operand_kind::Literal_kind:: + literal_spec_constant_op_integer: + state << indent(R"(value = static_cast<)") << op_enum_name + << indent(true, R"(>(words[word_index++]); +)"); + break; + } + break; + } + } + push_indent.finish(); + state << indent(R"(`return nullptr; +} +)"); + } + for(auto &instruction : top_level.instructions.instructions) + { + auto struct_name = get_enumerant_name(op_enum_name, instruction.opname, true); + auto parse_function_name = get_parse_instruction_function_name(instruction.opname); + state + << indent(R"(static std::unique_ptr )") << parse_function_name + << indent( + true, + R"((const Word *words, std::size_t word_count, Semantics &semantics, std::size_t error_instruction_index) +{ +`std::size_t word_index = 1; // skip opcode +)"); + auto push_indent2 = state.pushed_indent(); + state << indent << struct_name << " instruction;\n"; + if(!instruction.operands.empty()) + state << indent("std::unique_ptr parse_error;\n"); + for(auto &operand : instruction.operands.operands) + { + auto parse_operand_function_name = get_parse_operand_function_name(operand); + auto member_name = get_member_name_from_operand(operand); + switch(operand.quantifier) + { + case ast::Instructions::Instruction::Operands::Operand::Quantifier::none: + { + state + << indent(R"(parse_error = )") << parse_operand_function_name + << indent( + true, + R"((words, word_count, semantics, error_instruction_index, word_index, instruction.)") + << member_name << indent(true, R"(); +if(parse_error) +`return parse_error; +)"); + break; + } + case ast::Instructions::Instruction::Operands::Operand::Quantifier::optional: + { + state + << indent(R"(if(word_index < word_count) +{ +`instruction.)") << member_name + << indent(true, R"(.emplace(); +`parse_error = )") << parse_operand_function_name + << indent( + true, + R"((words, word_count, semantics, error_instruction_index, word_index, *instruction.)") + << member_name << indent(true, R"(); +`if(parse_error) +``return parse_error; +} +)"); + break; + } + case ast::Instructions::Instruction::Operands::Operand::Quantifier::variable: + { + state + << indent(R"(while(word_index < word_count) +{ +`instruction.)") << member_name + << indent(true, R"(.emplace_back(); +`parse_error = )") << parse_operand_function_name + << indent( + true, + R"((words, word_count, semantics, error_instruction_index, word_index, instruction.)") + << member_name << indent(true, R"(.back()); +`if(parse_error) +``return parse_error; +} +)"); + } + } + } + push_indent2.finish(); + state << indent(R"(`if(word_index < word_count) +``return semantics.handle_error(error_instruction_index + word_index, error_instruction_index, "extra words at end of instruction"); +`semantics.handle_instruction(std::move(instruction)); +`return nullptr; +} +)"); + } + state << indent( + R"(static std::unique_ptr parse_instruction(const Word *words, std::size_t word_count, Semantics &semantics, std::size_t error_instruction_index) +{ +`Op op = static_cast(words[0] & 0xFFFFU); +`switch(op) +`{ +)"); + for(auto &instruction : top_level.instructions.instructions) + { + auto push_indent2 = state.pushed_indent(2); + auto enumerant_name = get_enumerant_name(op_enum_name, instruction.opname, true); + auto parse_function_name = get_parse_instruction_function_name(instruction.opname); + state << indent("case ") << op_enum_name << "::" << enumerant_name + << indent(true, R"(: +`return )") << parse_function_name + << indent(true, R"((words, word_count, semantics, error_instruction_index); +)"); + } + state << indent(R"(`} +`return semantics.handle_error(error_instruction_index, error_instruction_index, json::ast::Number_value::append_unsigned_integer_to_string(static_cast(op), "unknown instruction: 0x", 0x10)); +} +static std::unique_ptr parse(const Word *words, std::size_t word_count, Semantics &semantics) +{ +`std::size_t word_index = 0; +`if(word_index >= word_count) +``return semantics.handle_error(word_index, 0, "hit EOF when parsing magic number"); +`if(words[word_index] != magic_number) +``return semantics.handle_error(word_index, 0, "invalid magic number"); +`word_index++; +`if(word_index >= word_count) +``return semantics.handle_error(word_index, 0, "hit EOF when parsing SPIR-V version"); +`if(words[word_index] & ~0xFFFF00UL) +``return semantics.handle_error(word_index, 0, "invalid SPIR-V version"); +`auto input_major_version = words[word_index] >> 16; +`auto input_minor_version = (words[word_index] >> 8) & 0xFFU; +`semantics.handle_spirv_version(input_major_version, input_minor_version); +`if(input_major_version != major_version || input_minor_version > minor_version) +``return semantics.handle_error(word_index, 0, "SPIR-V version not supported"); +`word_index++; +`if(word_index >= word_count) +``return semantics.handle_error(word_index, 0, "hit EOF when parsing generator's magic number"); +`semantics.handle_generator_magic_number(words[word_index++]); +`if(word_index >= word_count) +``return semantics.handle_error(word_index, 0, "hit EOF when parsing id bound"); +`semantics.handle_id_bound(words[word_index++]); +`if(word_index >= word_count) +``return semantics.handle_error(word_index, 0, "hit EOF when parsing SPIR-V shader header"); +`if(words[word_index] != 0) +``return semantics.handle_error(word_index, 0, "nonzero reserved word in SPIR-V shader header"); +`word_index++; +`// now we've finished reading the shader header, the rest of the shader is just instructions +`while(word_index < word_count) +`{ +``auto instruction_word_count = words[word_index] >> 16; +``if(instruction_word_count == 0) +```return semantics.handle_error(word_index, word_index, "invalid instruction"); +``if(word_index + instruction_word_count > word_count) +```return semantics.handle_error(word_index, word_index, "instruction longer than rest of shader"); +``auto parse_error = parse_instruction(words + word_index, instruction_word_count, semantics, word_index); +``if(parse_error) +```return parse_error; +``word_index += instruction_word_count; +`} +`return nullptr; +} +)"); + } + state << "};\n"; + write_namespaces_end(state, spirv_namespace_names); + write_file_guard_end(state); + } +}; + +struct Parser_source_generator final : public Generator +{ + Parser_source_generator() : Generator("parser.cpp") + { + } + virtual void run(Generator_args &generator_args, const ast::Top_level &top_level) const override + { + Generator_state state(this, generator_args, top_level); + state.open_output_file(); + write_file_comments(state, top_level.copyright); + state << "#include \"parser.h\"\n" + "#include \n" + "\n"; + write_namespaces_start(state, spirv_namespace_names); + state << indent(R"(namespace +{ +/** instantiate Parser with Parse_semantics_generic to help catch bugs */ +[[gnu::unused]] auto parser_test(const Word *words, std::size_t word_count, Parse_semantics_generic &semantics) +{ +`return Parser<>::parse(words, word_count, semantics); +} +} + +std::unique_ptr Parse_dump::handle_error(std::size_t word_index, std::size_t instruction_word_index, std::string message) +{ +`return std::unique_ptr(new Parse_error(word_index, instruction_word_index, std::move(message))); +} + +void Parse_dump::handle_spirv_version(unsigned major, unsigned minor) +{ +`os << "SPIR-V version " << major << "." << minor << "\n"; +} + +void Parse_dump::handle_generator_magic_number(Word value) +{ +`os << "generator magic number: " << json::ast::Number_value::append_unsigned_integer_to_string(value, "0x", 0x10) << "\n"; +} + +void Parse_dump::handle_id_bound(Word id_bound) +{ +`os << "id bound: " << json::ast::Number_value::unsigned_integer_to_string(id_bound) << "\n"; +} +)"); + for(auto &operand_kind : top_level.operand_kinds.operand_kinds) + { + auto dump_function_name = + Parser_header_generator::get_dump_operand_function_name(operand_kind); + { + state << indent(R"( +void Parse_dump::)") << dump_function_name + << "(const " << get_operand_with_parameters_name(state, operand_kind) + << R"( &value) +{ +)"; + auto push_indent = state.pushed_indent(); + switch(operand_kind.category) + { + case ast::Operand_kinds::Operand_kind::Category::bit_enum: + { + bool operand_has_any_parameters = + get_operand_has_any_parameters(state, operand_kind); + state << indent(R"(Word bits = static_cast(value)") + << (operand_has_any_parameters ? ".value" : "") << indent(true, R"(); +bool first = true; +)"); + auto enum_name = get_enum_name(operand_kind); + auto &enumerants = + util::get(operand_kind.value); + const ast::Operand_kinds::Operand_kind::Enumerants::Enumerant *zero_enumerant = + nullptr; + for(auto &enumerant : enumerants.enumerants) + { + if(enumerant.value == 0) + { + zero_enumerant = &enumerant; + continue; + } + auto enumerant_name = + get_enumerant_name(operand_kind.kind, enumerant.enumerant, false); + auto scoped_enumerant_name = enum_name + "::" + enumerant_name; + state << indent(R"(if((bits & static_cast()") << scoped_enumerant_name + << indent(true, R"()) == static_cast()") + << scoped_enumerant_name << indent(true, R"()) +{ +`bits &= ~static_cast()") + << scoped_enumerant_name << indent(true, R"(); +`if(first) +``first = false; +`else +``os << " | "; +`os << get_enumerant_name()") << scoped_enumerant_name + << indent(true, R"(); +)"); + if(!enumerant.parameters.empty()) + { + auto enumerant_member_name = get_member_name_from_enumerant(enumerant); + state << indent(R"(`os << "("; +`if(value.)") << enumerant_member_name + << indent(true, R"() +`{ +``auto ¶meters = *value.)") << enumerant_member_name + << indent(true, R"(; +)"); + bool first = true; + for(auto ¶meter : enumerant.parameters.parameters) + { + if(first) + first = false; + else + state << indent(R"(``os << ", "; +)"); + state << indent(2) + << Parser_header_generator::get_dump_operand_function_name( + parameter) + << "(parameters." << get_member_name_from_parameter(parameter) + << ");\n"; + } + state << indent(R"a(`} +`os << ")"; +)a"); + } + state << indent(R"(} +)"); + } + std::string zero_enumerant_name_code; + if(zero_enumerant) + { + zero_enumerant_name_code = + "get_enumerant_name(" + enum_name + "::" + + get_enumerant_name( + operand_kind.kind, zero_enumerant->enumerant, false) + + ")"; + } + else + { + zero_enumerant_name_code = "\"0\""; + } + state << indent(R"(if(bits != 0) +{ +`if(!first) +``os << " | "; +`os << json::ast::Number_value::append_unsigned_integer_to_string(bits, "0x", 0x10); +} +else if(first) +{ +`os << )") << zero_enumerant_name_code + << indent(true, R"(; +} +)"); + break; + } + case ast::Operand_kinds::Operand_kind::Category::value_enum: + { + auto enum_name = get_enum_name(operand_kind); + if(get_operand_has_any_parameters(state, operand_kind)) + { + state << indent(R"(switch(value.value) +{ +)"); + auto &enumerants = util::get( + operand_kind.value); + bool any_parameterless_enumerants = false; + for(auto &enumerant : enumerants.enumerants) + { + if(!enumerant.parameters.empty()) + continue; + any_parameterless_enumerants = true; + state << indent("case ") << enum_name + << "::" << get_enumerant_name( + operand_kind.kind, enumerant.enumerant, false) + << ":\n"; + } + if(any_parameterless_enumerants) + { + state << indent(R"(`os << get_enumerant_name(value.value); +`break; +)"); + } + for(auto &enumerant : enumerants.enumerants) + { + if(enumerant.parameters.empty()) + continue; + auto enumerant_name = + get_enumerant_name(operand_kind.kind, enumerant.enumerant, false); + auto scoped_enumerant_name = enum_name + "::" + enumerant_name; + auto enumerant_parameters_struct_name = + get_enumerant_parameters_struct_name( + operand_kind.kind, enumerant.enumerant, false); + state << indent("case ") << scoped_enumerant_name << indent(true, R"(: +{ +`os << get_enumerant_name()") << scoped_enumerant_name + << indent(true, R"() << "("; +`auto *parameters = util::get_if<)") + << enumerant_parameters_struct_name + << indent(true, R"(>(&value.parameters); +`if(parameters) +`{ +)"); + bool first = true; + for(auto ¶meter : enumerant.parameters.parameters) + { + if(first) + first = false; + else + state << indent(R"(``os << ", "; +)"); + state << indent(2) + << Parser_header_generator::get_dump_operand_function_name( + parameter) + << "(parameters->" + << get_member_name_from_parameter(parameter) << ");\n"; + } + state << indent(R"a(`} +`os << ")"; +`break; +} +)a"); + } + state << indent(R"(default: +`os << json::ast::Number_value::unsigned_integer_to_string(static_cast(value.value)); +} +)"); + } + else + { + state << indent(R"(if(util::Enum_traits<)") << enum_name + << indent(true, R"(>::find_value(value) == util::Enum_traits<)") + << enum_name << indent(true, R"(>::npos) +`os << json::ast::Number_value::unsigned_integer_to_string(static_cast(value)); +else +`os << get_enumerant_name(value); +)"); + } + break; + } + case ast::Operand_kinds::Operand_kind::Category::composite: + { + auto &bases = + util::get(operand_kind.value); + state << indent("os << \"{\";\n"); + for(std::size_t i = 0; i < bases.values.size(); i++) + { + if(i != 0) + { + state << indent("os << \", \";\n"); + } + state << indent << Parser_header_generator::get_dump_operand_function_name( + bases.values[i]) + << "(value." + << json::ast::Number_value::append_unsigned_integer_to_string(i + 1, + "part_") + << ");\n"; + } + state << indent("os << \"}\";\n"); + break; + } + case ast::Operand_kinds::Operand_kind::Category::id: + { + state << indent( + R"(os << json::ast::Number_value::append_unsigned_integer_to_string(value, "#"); +)"); + break; + } + case ast::Operand_kinds::Operand_kind::Category::literal: + { + auto literal_kind = + ast::Operand_kinds::Operand_kind::get_literal_kind_from_json_name( + operand_kind.kind); + if(!literal_kind) + throw Generate_error("bad literal kind"); + switch(*literal_kind) + { + case ast::Operand_kinds::Operand_kind::Literal_kind::literal_integer: + state << indent( + R"(os << json::ast::Number_value::append_unsigned_integer_to_string(value, "0x"); +)"); + break; + case ast::Operand_kinds::Operand_kind::Literal_kind::literal_string: + state << indent( + R"(json::ast::String_value::write(os, value); +)"); + break; + case ast::Operand_kinds::Operand_kind::Literal_kind:: + literal_context_dependent_number: + state << indent( + R"(static_assert(std::is_same &>::value, "missing dump code for operand kind"); +auto separator = ""; +os << "{"; +for(Word word : value) +{ +`os << separator; +`separator = ", "; +`os << json::ast::Number_value::append_unsigned_integer_to_string(word, "0x", 0x10, 8); +} +os << "}"; +)"); + break; + case ast::Operand_kinds::Operand_kind::Literal_kind::literal_ext_inst_integer: + state << indent( + R"(os << json::ast::Number_value::append_unsigned_integer_to_string(value, "0x"); +)"); + break; + case ast::Operand_kinds::Operand_kind::Literal_kind:: + literal_spec_constant_op_integer: + state << indent(R"(if(util::Enum_traits<)") << op_enum_name + << indent(true, R"(>::find_value(value) == util::Enum_traits<)") + << op_enum_name << indent(true, R"(>::npos) +`os << json::ast::Number_value::unsigned_integer_to_string(static_cast(value)); +else +`os << get_enumerant_name(value); +)"); + break; + } + break; + } + } + push_indent.finish(); + state << indent("}\n"); + } + state << indent(R"( +void Parse_dump::)") + << dump_function_name << "(const util::optional<" + << get_operand_with_parameters_name(state, operand_kind) + << indent(true, R"(> &value) +{ +`if(value) +)") << indent(2) << dump_function_name + << indent(true, R"((*value); +`else +``os << "nullopt"; +} + +void Parse_dump::)") + << dump_function_name << "(const std::vector<" + << get_operand_with_parameters_name(state, operand_kind) + << indent(true, R"(> &values) +{ +`auto separator = ""; +`os << "{"; +`for(auto &value : values) +`{ +``os << separator; +``separator = ", "; +)") << indent(2) << dump_function_name + << indent(true, R"((value); +`} +`os << "}"; +} +)"); + } + for(auto &instruction : top_level.instructions.instructions) + { + auto struct_name = get_enumerant_name(op_enum_name, instruction.opname, true); + state << indent( + "\n" + "void Parse_dump::handle_instruction(") + << struct_name << indent(true, R"( instruction) +{ +`os << ")") << instruction.opname + << indent(true, R"(\n"; +)"); + for(auto &operand : instruction.operands.operands) + { + auto push_indent = state.pushed_indent(); + auto member_name = get_member_name_from_operand(operand); + state << indent("os << \" ") << member_name << indent(true, R"(:"; +)") << indent << Parser_header_generator::get_dump_operand_function_name(operand.kind) + << indent(true, R"((instruction.)") << member_name << indent(true, R"(); +os << "\n"; +)"); + } + state << indent("}\n"); + } + write_namespaces_end(state, spirv_namespace_names); + } +}; + +std::unique_ptr Generators::make_spirv_header_generator() +{ + return std::unique_ptr(new Spirv_header_generator); +} + +std::unique_ptr Generators::make_spirv_source_generator() +{ + return std::unique_ptr(new Spirv_source_generator); +} + +std::unique_ptr Generators::make_parser_header_generator() +{ + return std::unique_ptr(new Parser_header_generator); +} + +std::unique_ptr Generators::make_parser_source_generator() +{ + return std::unique_ptr(new Parser_source_generator); +} + +std::vector> Generators::make_all_generators() +{ + std::unique_ptr generators_array[] = { + make_spirv_header_generator(), + make_spirv_source_generator(), + make_parser_header_generator(), + make_parser_source_generator(), + }; + // use array then move because you can't move out of an std::initializer_list + std::vector> retval; + retval.reserve(sizeof(generators_array) / sizeof(generators_array[0])); + for(auto &generator : generators_array) + retval.push_back(std::move(generator)); + return retval; +} +} +} +} diff --git a/src/generate_spirv_parser/generate-old.h b/src/generate_spirv_parser/generate-old.h new file mode 100644 index 0000000..1a40f76 --- /dev/null +++ b/src/generate_spirv_parser/generate-old.h @@ -0,0 +1,474 @@ +/* + * 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_GENERATE_OLD_H_ +#define GENERATE_SPIRV_PARSER_GENERATE_OLD_H_ + +#include "ast.h" +#include "util/string_view.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vulkan_cpu +{ +namespace generate_spirv_parser +{ +namespace generate +{ +struct Generate_error : public std::runtime_error +{ + using runtime_error::runtime_error; +}; + +class Generator +{ +private: + struct Tester; + +public: + struct Generator_args + { + std::string output_directory; + explicit Generator_args(std::string output_directory) noexcept + : output_directory(std::move(output_directory)) + { + } + Generator_args(Generator_args &&) = default; + Generator_args &operator=(Generator_args &&) = default; + Generator_args(const Generator_args &) = delete; + Generator_args &operator=(const Generator_args &) = delete; + }; + +protected: + class Push_indent; + struct Generator_state + { + Generator_args &generator_args; + std::size_t indent_level; + std::string full_output_file_name; + std::string guard_macro_name; + std::ofstream os; + const ast::Top_level &top_level; + std::unordered_map operand_kind_map; + std::unordered_map + operand_has_any_parameters_map; + explicit Generator_state(const Generator *generator, + Generator_args &generator_args, + const ast::Top_level &top_level); + void open_output_file(); + template ())> + Generator_state &operator<<(T &&v) + { + os << std::forward(v); + return *this; + } + Generator_state &operator<<(const ast::Capabilities &v) + { + write_capabilities_set(*this, v); + return *this; + } + Generator_state &operator<<(const ast::Extensions &v) + { + write_extensions_set(*this, v); + return *this; + } + Push_indent pushed_indent(std::ptrdiff_t amount = 1) noexcept; + }; + class Push_indent final + { + Push_indent(const Push_indent &) = delete; + Push_indent &operator=(const Push_indent &) = delete; + + private: + Generator_state *state; + std::ptrdiff_t amount; + + public: + explicit Push_indent(Generator_state &state, std::ptrdiff_t amount = 1) noexcept + : state(&state), + amount(amount) + { + state.indent_level += amount; + } + Push_indent(Push_indent &&rt) noexcept : state(rt.state), amount(rt.amount) + { + rt.state = nullptr; + } + void finish() noexcept + { + assert(state); + state->indent_level -= amount; + state = nullptr; + } + ~Push_indent() + { + if(state) + state->indent_level -= amount; + } + }; + // translates initial '`' (backtick) characters to indentations + struct Indent_interpreted_text + { + const char *text; + std::ptrdiff_t indent_offset; + bool start_indented; + constexpr explicit Indent_interpreted_text(const char *text, + std::ptrdiff_t indent_offset, + bool start_indented) noexcept + : text(text), + indent_offset(indent_offset), + start_indented(start_indented) + { + } + friend Generator_state &operator<<(Generator_state &state, Indent_interpreted_text v) + { + write_indent_interpreted_text(state, v.text, v.indent_offset, v.start_indented); + return state; + } + }; + struct Indent_t + { + std::ptrdiff_t offset; + explicit Indent_t() = default; + constexpr Indent_t operator()(std::ptrdiff_t additional_offset) const noexcept + { + return Indent_t{offset + additional_offset}; + } + constexpr Indent_interpreted_text operator()(const char *text) const noexcept + { + return Indent_interpreted_text(text, offset, false); + } + constexpr Indent_interpreted_text operator()(bool start_indented, const char *text) const + noexcept + { + return Indent_interpreted_text(text, offset, start_indented); + } + friend Generator_state &operator<<(Generator_state &state, Indent_t indent) + { + write_indent(state, indent.offset); + return state; + } + }; + static constexpr auto indent = Indent_t{0}; + enum class Integer_literal_base + { + dec = 0, + hex, + oct + }; + struct Unsigned_integer_literal + { + std::uint64_t value; + Integer_literal_base base; + std::size_t minimum_digit_count; + constexpr Unsigned_integer_literal(std::uint64_t value, + Integer_literal_base base, + std::size_t minimum_digit_count = 1) noexcept + : value(value), + base(base), + minimum_digit_count(minimum_digit_count) + { + } + friend Generator_state &operator<<(Generator_state &state, Unsigned_integer_literal v) + { + write_unsigned_integer_literal(state, v.value, v.base, v.minimum_digit_count); + return state; + } + }; + static constexpr Unsigned_integer_literal unsigned_dec_integer_literal( + std::uint64_t value) noexcept + { + return Unsigned_integer_literal(value, Integer_literal_base::dec); + } + static constexpr Unsigned_integer_literal unsigned_hex_integer_literal( + std::uint64_t value, std::size_t minimum_digit_count = 1) noexcept + { + return Unsigned_integer_literal(value, Integer_literal_base::hex, minimum_digit_count); + } + static constexpr Unsigned_integer_literal unsigned_oct_integer_literal( + std::uint64_t value, std::size_t minimum_digit_count = 1) noexcept + { + return Unsigned_integer_literal(value, Integer_literal_base::oct, minimum_digit_count); + } + struct Signed_integer_literal + { + std::int64_t value; + constexpr explicit Signed_integer_literal(std::int64_t value) noexcept : value(value) + { + } + friend Generator_state &operator<<(Generator_state &state, Signed_integer_literal v) + { + write_signed_integer_literal(state, v.value); + return state; + } + }; + static constexpr Signed_integer_literal signed_integer_literal(std::int64_t value) noexcept + { + return Signed_integer_literal(value); + } + +protected: + const char *const output_base_file_name; + +protected: + static std::string get_guard_macro_name_from_file_name(std::string file_name); + static std::string get_enumerant_name(util::string_view enumeration_name, + std::string enumerant_name, + bool input_name_should_have_prefix) + { + return get_enumerant_name(enumeration_name.data(), + enumeration_name.size(), + std::move(enumerant_name), + input_name_should_have_prefix); + } + static std::string get_enumerant_name(const char *enumeration_name, + std::size_t enumeration_name_size, + std::string enumerant_name, + bool input_name_should_have_prefix); + static void write_indent_absolute(Generator_state &state, std::size_t amount); + static void write_indent(Generator_state &state, std::ptrdiff_t offset) + { + write_indent_absolute(state, state.indent_level + offset); + } + static void write_indent_interpreted_text(Generator_state &state, + const char *text, + std::ptrdiff_t offset, + bool start_indented); + static void write_automatically_generated_file_warning(Generator_state &state); + static void write_copyright_comment(Generator_state &state, const ast::Copyright ©right); + static void write_file_comments(Generator_state &state, const ast::Copyright ©right) + { + write_automatically_generated_file_warning(state); + write_copyright_comment(state, copyright); + } + static void write_file_guard_start(Generator_state &state); + static void write_file_guard_end(Generator_state &state); + static void write_namespace_start(Generator_state &state, const char *namespace_name); + static void write_namespace_start(Generator_state &state, const std::string &namespace_name); + +private: + static void write_namespace_end(Generator_state &state); + +protected: + static void write_namespace_end(Generator_state &state, const char *namespace_name) + { + write_namespace_end(state); + } + static void write_namespace_end(Generator_state &state, const std::string &namespace_name) + { + write_namespace_end(state); + } + static void write_namespaces_start(Generator_state &state, + const char *const *namespace_names, + std::size_t namespace_name_count) + { + for(std::size_t i = 0; i < namespace_name_count; i++) + write_namespace_start(state, namespace_names[i]); + } + static void write_namespaces_start(Generator_state &state, + const std::string *namespace_names, + std::size_t namespace_name_count) + { + for(std::size_t i = 0; i < namespace_name_count; i++) + write_namespace_start(state, namespace_names[i]); + } + static void write_namespaces_end(Generator_state &state, + const char *const *namespace_names, + std::size_t namespace_name_count) + { + for(std::size_t i = 0; i < namespace_name_count; i++) + write_namespace_end(state, namespace_names[namespace_name_count - i - 1]); + state << '\n'; + } + static void write_namespaces_end(Generator_state &state, + const std::string *namespace_names, + std::size_t namespace_name_count) + { + for(std::size_t i = 0; i < namespace_name_count; i++) + write_namespace_end(state, namespace_names[namespace_name_count - i - 1]); + state << '\n'; + } + template + static void write_namespaces_start(Generator_state &state, const T(&namespace_names)[N]) + { + write_namespaces_start(state, namespace_names, N); + } + template + static void write_namespaces_end(Generator_state &state, const T(&namespace_names)[N]) + { + write_namespaces_end(state, namespace_names, N); + } + static void write_namespaces_start(Generator_state &state, + std::initializer_list namespace_names) + { + write_namespaces_start(state, namespace_names.begin(), namespace_names.size()); + } + static void write_namespaces_start(Generator_state &state, + std::initializer_list namespace_names) + { + write_namespaces_start(state, namespace_names.begin(), namespace_names.size()); + } + static void write_namespaces_end(Generator_state &state, + std::initializer_list namespace_names) + { + write_namespaces_end(state, namespace_names.begin(), namespace_names.size()); + } + static void write_namespaces_end(Generator_state &state, + std::initializer_list namespace_names) + { + write_namespaces_end(state, namespace_names.begin(), namespace_names.size()); + } + static void write_unsigned_integer_literal(Generator_state &state, + std::uint64_t value, + Integer_literal_base base, + std::size_t minimum_digit_count); + static void write_signed_integer_literal(Generator_state &state, std::int64_t value); + +private: + struct Get_extensions_visitor; + +protected: + static std::unordered_set get_extensions(const ast::Top_level &top_level); + static void write_capabilities_set(Generator_state &state, + const ast::Capabilities &capabilities); + static void write_extensions_set(Generator_state &state, const ast::Extensions &extensions); + static std::string get_name_from_words(const std::string &words); + static std::string get_member_name_from_operand( + const ast::Instructions::Instruction::Operands::Operand &operand); + static std::string get_member_name_from_parameter( + const ast::Operand_kinds::Operand_kind::Enumerants::Enumerant::Parameters::Parameter + ¶meter); + static std::string get_member_name_from_enumerant( + const ast::Operand_kinds::Operand_kind::Enumerants::Enumerant + &enumerant); + static const ast::Operand_kinds::Operand_kind &get_operand_kind_from_string( + Generator_state &state, const std::string &operand_kind_str) + { + auto *retval = state.operand_kind_map[operand_kind_str]; + if(!retval) + throw Generate_error("operand kind not found: " + operand_kind_str); + return *retval; + } + static bool get_operand_has_any_parameters(Generator_state &state, + const ast::Operand_kinds::Operand_kind &operand_kind) + { + return state.operand_has_any_parameters_map[&operand_kind]; + } + static std::string get_enumerant_parameters_struct_name(util::string_view enumeration_name, + std::string enumerant_name, + bool input_name_should_have_prefix) + { + auto retval = "_" + get_enumerant_name( + enumeration_name, enumerant_name, input_name_should_have_prefix) + + "_parameters"; + retval.insert(retval.begin(), enumeration_name.begin(), enumeration_name.end()); + return retval; + } + static std::string get_operand_with_parameters_name( + Generator_state &state, const ast::Operand_kinds::Operand_kind &operand_kind); + static std::string get_operand_with_parameters_name(Generator_state &state, + const std::string &operand_kind_str) + { + return get_operand_with_parameters_name( + state, get_operand_kind_from_string(state, operand_kind_str)); + } + static std::string get_operand_with_parameters_name( + Generator_state &state, const ast::Instructions::Instruction::Operands::Operand &operand) + { + return get_operand_with_parameters_name(state, + get_operand_kind_from_string(state, operand.kind)); + } + static std::string get_enum_name(std::string operand_kind_str) + { + return operand_kind_str; + } + static std::string get_enum_name(const ast::Operand_kinds::Operand_kind &operand_kind) + { + return get_enum_name(operand_kind.kind); + } + static void write_struct_nonstatic_members_and_constructors(Generator_state &state, + const std::string &struct_name, + const std::string *member_types, + const std::string *member_names, + std::size_t member_count); + static std::vector + get_unique_enumerants( + std::vector enumerants); + +protected: + static constexpr const char *vulkan_cpu_namespace_name = "vulkan_cpu"; + static constexpr const char *spirv_namespace_name = "spirv"; + static constexpr const char *spirv_namespace_names[] = { + vulkan_cpu_namespace_name, spirv_namespace_name, + }; + static constexpr const char *capability_enum_name = "Capability"; + static constexpr const char *extension_enum_name = "Extension"; + static constexpr const char *op_enum_name = "Op"; + +public: + explicit Generator(const char *output_base_file_name) noexcept + : output_base_file_name(output_base_file_name) + { + } + virtual void run(Generator_args &generator_args, const ast::Top_level &top_level) const = 0; + void run(Generator_args &&generator_args, const ast::Top_level &top_level) const + { + run(generator_args, top_level); + } + +public: + virtual ~Generator() = default; +}; + +inline Generator::Push_indent Generator::Generator_state::pushed_indent( + std::ptrdiff_t amount) noexcept +{ + return Push_indent(*this, amount); +} + +struct Spirv_header_generator; +struct Spirv_source_generator; +struct Parser_header_generator; +struct Parser_source_generator; + +struct Generators +{ + static std::unique_ptr make_spirv_header_generator(); + static std::unique_ptr make_spirv_source_generator(); + static std::unique_ptr make_parser_header_generator(); + static std::unique_ptr make_parser_source_generator(); + static std::vector> make_all_generators(); +}; +} +} +} + +#endif /* GENERATE_SPIRV_PARSER_GENERATE_OLD_H_ */ diff --git a/src/generate_spirv_parser/generate.cpp b/src/generate_spirv_parser/generate.cpp index 7a07d35..5ca92b3 100644 --- a/src/generate_spirv_parser/generate.cpp +++ b/src/generate_spirv_parser/generate.cpp @@ -23,10 +23,13 @@ #include "generate.h" #include "json/json.h" #include "util/optional.h" +#include "util/filesystem.h" #include #include #include #include +#include +#include #include namespace vulkan_cpu @@ -35,2300 +38,585 @@ namespace generate_spirv_parser { namespace generate { -Generator::Generator_state::Generator_state(const Generator *generator, - Generator_args &generator_args, - const ast::Top_level &top_level) - : generator_args(generator_args), - indent_level(0), - full_output_file_name(generator_args.output_directory + "/" - + generator->output_base_file_name), - guard_macro_name(get_guard_macro_name_from_file_name(full_output_file_name)), - os(), - top_level(top_level), - operand_kind_map(), - operand_has_any_parameters_map() -{ - os.exceptions(std::ios::badbit | std::ios::failbit); - for(auto &operand_kind : top_level.operand_kinds.operand_kinds) - { - operand_kind_map.emplace(operand_kind.kind, &operand_kind); - bool &has_any_parameters = operand_has_any_parameters_map[&operand_kind]; - has_any_parameters = false; - if(util::holds_alternative( - operand_kind.value)) - { - auto &enumerants = - util::get(operand_kind.value); - for(auto &enumerant : enumerants.enumerants) - { - if(!enumerant.parameters.empty()) - { - has_any_parameters = true; - break; - } - } - } - } -} - -void Generator::Generator_state::open_output_file() -{ - os.open(full_output_file_name); -} - -constexpr Generator::Indent_t Generator::indent; -constexpr const char *Generator::vulkan_cpu_namespace_name; -constexpr const char *Generator::spirv_namespace_name; -constexpr const char *Generator::spirv_namespace_names[]; -constexpr const char *Generator::extension_enum_name; -constexpr const char *Generator::capability_enum_name; -constexpr const char *Generator::op_enum_name; +using namespace util::string_view_literals; -std::string Generator::get_guard_macro_name_from_file_name(std::string file_name) +struct Spirv_and_parser_generator : public Generator { - auto retval = std::move(file_name); - for(char &ch : retval) - { - if(ch >= 'a' && ch <= 'z') - { - ch = ch - 'a' + 'A'; // convert to uppercase - continue; - } - if(ch >= 'A' && ch <= 'Z') - continue; - if(ch >= '0' && ch <= '9') - continue; - ch = '_'; - } - retval += '_'; - if(retval[0] >= '0' && retval[0] <= '9') - retval.insert(0, 1, '_'); - for(std::size_t double_underline_index = retval.find("__"); - double_underline_index != std::string::npos; - double_underline_index = retval.find("__", double_underline_index + 1)) - { - // insert a u in all pairs of underlines to prevent generating a reserved identifier - retval.insert(++double_underline_index, "u"); - } - if(retval.size() >= 2 && retval[0] == '_' && retval[1] >= 'A' && retval[1] <= 'Z') - { - // insert a u to prevent generating a reserved identifier: starting with an underline and a - // capital letter - retval.insert(1, "u"); - } - return retval; -} + struct State; + virtual void run(Generator_args &generator_args, + const ast::Top_level &top_level) const override; +}; namespace { -constexpr bool is_uppercase_letter(char ch) noexcept -{ - if(ch >= 'A' && ch <= 'Z') - return true; - return false; -} - -constexpr bool is_lowercase_letter(char ch) noexcept -{ - if(ch >= 'a' && ch <= 'z') - return true; - return false; -} - -constexpr bool is_letter(char ch) noexcept -{ - return is_uppercase_letter(ch) || is_lowercase_letter(ch); } -constexpr bool is_identifier_start(char ch) noexcept +struct Spirv_and_parser_generator::State { - return is_letter(ch) || ch == '_'; -} - -constexpr bool is_digit(char ch) noexcept -{ - if(ch >= '0' && ch <= '9') - return true; - return false; -} -} - -std::string Generator::get_enumerant_name(const char *enumeration_name, - std::size_t enumeration_name_size, - std::string enumerant_name, - bool input_name_should_have_prefix) -{ - bool starts_with_enumeration_name = - enumerant_name.compare(0, enumeration_name_size, enumeration_name, enumeration_name_size) - == 0; - bool starts_with_doubled_enumeration_name = false; - if(starts_with_enumeration_name) - starts_with_doubled_enumeration_name = enumerant_name.compare(enumeration_name_size, - enumeration_name_size, - enumeration_name, - enumeration_name_size) - == 0; - std::size_t needed_prefix_count; - if(input_name_should_have_prefix) - { - if(!starts_with_enumeration_name) - needed_prefix_count = 2; - else if(starts_with_doubled_enumeration_name) - needed_prefix_count = 1; - else - needed_prefix_count = 0; - } - else + class Generated_output_stream { - if(starts_with_enumeration_name) - needed_prefix_count = 1; // ensure that we don't end up with name collisions - else if(enumerant_name.empty()) - needed_prefix_count = 1; // return something other than the empty string - else - needed_prefix_count = is_identifier_start(enumerant_name[0]) ? 0 : 1; - } - for(std::size_t i = 0; i < needed_prefix_count; i++) - enumerant_name.insert(0, enumeration_name, enumeration_name_size); - return enumerant_name; -} - -void Generator::write_indent_absolute(Generator_state &state, std::size_t amount) -{ - static constexpr auto indent_string = " "; - for(std::size_t i = 0; i < amount; i++) - state << indent_string; -} + private: + std::deque value; + util::filesystem::path file_path; -void Generator::write_indent_interpreted_text(Generator_state &state, - const char *text, - std::ptrdiff_t offset, - bool start_indented) -{ - bool did_indent = start_indented; - std::size_t indent_amount = offset + state.indent_level; - for(; *text; text++) - { - auto &ch = *text; - if(ch == '\n') + public: + explicit Generated_output_stream(util::filesystem::path file_path) noexcept + : value(), + file_path(std::move(file_path)) { - state << ch; - did_indent = false; - indent_amount = offset + state.indent_level; } - else if(!did_indent && ch == '`') + const util::filesystem::path &get_file_path() const noexcept { - indent_amount++; + return file_path; } - else + 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(!did_indent) + if(output_tab_width != output_tab_width_no_tabs_allowed) { - did_indent = true; - write_indent_absolute(state, indent_amount); + while(indent_depth >= output_tab_width) + { + indent_depth -= output_tab_width; + write_char('\t'); + } } - state << ch; - } - } -} - -void Generator::write_automatically_generated_file_warning(Generator_state &state) -{ - state - << "/* This file is automatically generated by generate_spirv_parser. DO NOT MODIFY. */\n"; -} - -void Generator::write_copyright_comment(Generator_state &state, const ast::Copyright ©right) -{ - state << "/*\n"; - for(auto &line : copyright.lines) - { - if(line.empty()) - { - state << " *\n"; - continue; - } - state << " * "; - bool was_last_star = false; - for(char ch : line) - { - if(was_last_star && ch == '/') - state << ' '; - was_last_star = (ch == '*'); - state << ch; - } - state << "\n"; - } - state << " */\n"; -} - -void Generator::write_file_guard_start(Generator_state &state) -{ - state << "#ifndef " << state.guard_macro_name << R"( -#define )" - << state.guard_macro_name << "\n" - "\n"; -} - -void Generator::write_file_guard_end(Generator_state &state) -{ - state << "#endif /* " << state.guard_macro_name << " */\n"; -} - -void Generator::write_namespace_start(Generator_state &state, const char *namespace_name) -{ - state << "namespace " << namespace_name << "\n" - "{\n"; -} - -void Generator::write_namespace_start(Generator_state &state, const std::string &namespace_name) -{ - state << "namespace " << namespace_name << "\n" - "{\n"; -} - -void Generator::write_namespace_end(Generator_state &state) -{ - state << "}\n"; -} - -void Generator::write_unsigned_integer_literal(Generator_state &state, - std::uint64_t value, - Integer_literal_base base, - std::size_t minimum_digit_count) -{ - constexpr std::uint64_t max_unsigned_value = std::numeric_limits::max(); - constexpr std::uint64_t max_unsigned_long_value = std::numeric_limits::max(); - auto literal_type = - value <= max_unsigned_value ? "U" : value <= max_unsigned_long_value ? "UL" : "ULL"; - auto number_prefix = ""; - unsigned base_as_number = 10; - switch(base) - { - case Integer_literal_base::dec: - minimum_digit_count = 1; - break; - case Integer_literal_base::hex: - base_as_number = 0x10; - number_prefix = "0x"; - break; - case Integer_literal_base::oct: - base_as_number = 010; - number_prefix = "0"; - break; - } - auto number_string = json::ast::Number_value::append_unsigned_integer_to_string( - value, number_prefix, base_as_number, minimum_digit_count) - + literal_type; - state << number_string; -} - -void Generator::write_signed_integer_literal(Generator_state &state, std::int64_t value) -{ - constexpr std::int64_t max_int_value = std::numeric_limits::max(); - constexpr std::int64_t min_int_value = std::numeric_limits::min(); - constexpr std::int64_t max_long_value = std::numeric_limits::max(); - constexpr std::int64_t min_long_value = std::numeric_limits::min(); - auto literal_type = ""; - if(value < min_int_value || value > max_int_value) - literal_type = "L"; - if(value < min_long_value || value > max_long_value) - literal_type = "LL"; - state << value << literal_type; -} - -struct Generator::Get_extensions_visitor -{ - std::unordered_set &retval; - constexpr Get_extensions_visitor(std::unordered_set &retval) noexcept - : retval(retval) - { - } - template - void operator()(const T &) - { - } - void operator()(const ast::Extensions &extensions) - { - for(auto &extension : extensions.extensions) - retval.insert(extension); - } -}; - -std::unordered_set Generator::get_extensions(const ast::Top_level &top_level) -{ - std::unordered_set retval; - top_level.visit(Get_extensions_visitor(retval)); - return retval; -} - -void Generator::write_capabilities_set(Generator_state &state, - const ast::Capabilities &capabilities) -{ - state << "util::Enum_set<" << capability_enum_name << ">{"; - auto separator = ""; - for(auto &capability : capabilities.capabilities) - { - state << separator << capability_enum_name - << "::" << get_enumerant_name(capability_enum_name, capability, false); - separator = ", "; - } - state << "}"; -} - -void Generator::write_extensions_set(Generator_state &state, const ast::Extensions &extensions) -{ - state << "util::Enum_set<" << extension_enum_name << ">{"; - auto separator = ""; - for(auto &extension : extensions.extensions) - { - state << separator << extension_enum_name - << "::" << get_enumerant_name(extension_enum_name, extension, false); - separator = ", "; - } - state << "}"; -} - -std::string Generator::get_name_from_words(const std::string &words) -{ - enum class Char_class - { - Uppercase, - OtherIdentifier, - WordSeparator, - }; - auto get_char_class = [](char ch) -> Char_class - { - if(is_uppercase_letter(ch)) - return Char_class::Uppercase; - if(is_letter(ch) || is_digit(ch)) - return Char_class::OtherIdentifier; - return Char_class::WordSeparator; - }; - auto find_words = [&](auto found_word_callback) -> void - { - util::optional word_start; - auto finish_word = [&](std::size_t index) - { - found_word_callback(util::string_view(words.data() + *word_start, index - *word_start)); - word_start = {}; - }; - auto start_word = [&](std::size_t index) - { - word_start = index; - }; - auto last_char_class = Char_class::WordSeparator; - for(std::size_t i = 0; i < words.size(); i++) - { - auto current_char_class = get_char_class(words[i]); - if(word_start) + 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) { - switch(current_char_class) + 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()) { - case Char_class::WordSeparator: - finish_word(i); - break; - case Char_class::Uppercase: - if(last_char_class != Char_class::Uppercase) + if(*iter == '\n') { - finish_word(i); - start_word(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++; + } + else if(is_at_start_of_line) + { + switch(*iter) + { + 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++) + { + assert(iter != value.end()); + assert(*iter == indent_indicator_char); + ++iter; + } + indent_depth += output_indent_width; + continue; + case escape_char: + { + ++iter; + assert(iter != value.end()); + if(*iter != escape_char) + { + if(*iter >= 'a' && *iter <= 'z') + { + 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 + { + 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; + start_indent_depth += output_indent_width; + indent_depth += output_indent_width; + continue; + } + assert(false); + continue; + } + break; + } + } + write_indent( + [&](char ch) + { + os << ch; + }, + indent_depth, + output_tab_width); + is_at_start_of_line = false; + os << *iter++; } - else if(i + 1 < words.size() - && get_char_class(words[i + 1]) == Char_class::OtherIdentifier) + else { - finish_word(i); - start_word(i); + os << *iter++; } - break; - case Char_class::OtherIdentifier: - break; } } - else if(current_char_class != Char_class::WordSeparator) + else { - start_word(i); + for(char ch : value) + os << ch; } - last_char_class = current_char_class; + os.close(); // manually close to not hide error exceptions } - if(word_start) - finish_word(words.size()); - }; - std::size_t retval_size = 0; - bool first = true; - find_words([&](util::string_view word) - { - if(!first) - retval_size++; // separating '_' - first = false; - retval_size += word.size(); - }); - std::string retval; - retval.reserve(retval_size); - first = true; - find_words([&](util::string_view word) - { - if(!first) - retval += '_'; - first = false; - retval += word; - }); - for(char &ch : retval) - { - if(is_uppercase_letter(ch)) - ch = ch - 'A' + 'a'; // to lowercase - } - static constexpr const char *const reserved_words[] = { - "alignas", - "alignof", - "and", - "and_eq", - "asm", - "atomic_cancel", - "atomic_commit", - "atomic_noexcept", - "auto", - "bitand", - "bitor", - "bool", - "break", - "case", - "catch", - "char", - "char16_t", - "char32_t", - "class", - "compl", - "concept", - "concepts", - "const", - "const_cast", - "constexpr", - "continue", - "decltype", - "default", - "delete", - "do", - "double", - "dynamic_cast", - "else", - "enum", - "explicit", - "export", - "extern", - "false", - "float", - "for", - "friend", - "goto", - "if", - "import", - "inline", - "int", - "long", - "module", - "modules", - "mutable", - "namespace", - "new", - "noexcept", - "not", - "not_eq", - "nullptr", - "operator", - "or", - "or_eq", - "private", - "protected", - "public", - "register", - "reinterpret_cast", - "requires", - "return", - "short", - "signed", - "sizeof", - "static", - "static_assert", - "static_cast", - "struct", - "switch", - "synchronized", - "template", - "this", - "thread_local", - "throw", - "true", - "try", - "typedef", - "typeid", - "typename", - "union", - "unsigned", - "using", - "virtual", - "void", - "volatile", - "wchar_t", - "while", - "xor", - "xor_eq", - }; - for(const char *reserved_word : reserved_words) - { - if(retval == reserved_word) + void write_unsigned_integer(std::uint64_t value, + unsigned base = json::ast::Number_value::default_base, + std::size_t min_length = 1) { - retval += '_'; - break; + 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--; + } + 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); } - } - return retval; -} - -#if 0 -#warning testing Generator::get_name_from_words -struct Generator::Tester -{ - struct Test_runner - { - Test_runner() + void write_signed_integer(std::int64_t value, + unsigned base = json::ast::Number_value::default_base) { - test(); - std::exit(1); + 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); } - }; - static Test_runner test_runner; - static void test() - { - for(auto &input : { - "abc def", "AbcDef", "ABCDef", "'abc, def'", - }) + void write_literal(util::string_view value) { - std::cout << "\"" << input << "\" -> " << get_name_from_words(input) - << std::endl; + *this << escape_char; + *this << literal_command; + write_unsigned_integer(value.size()); + *this << escape_char; + *this << value; + *this << escape_char; } - } -}; - -Generator::Tester::Test_runner Generator::Tester::test_runner; -#endif - -std::string Generator::get_member_name_from_operand( - const ast::Instructions::Instruction::Operands::Operand &operand) -{ - if(!operand.name.empty()) - return get_name_from_words(operand.name); - util::string_view id_str = "Id"; - if(util::string_view(operand.kind).compare(0, id_str.size(), id_str) == 0 - && id_str.size() < operand.kind.size() - && is_uppercase_letter(operand.kind[id_str.size()])) - return get_name_from_words(operand.kind.substr(id_str.size())); - return get_name_from_words(operand.kind); -} - -std::string Generator::get_member_name_from_parameter( - const ast::Operand_kinds::Operand_kind::Enumerants::Enumerant::Parameters::Parameter ¶meter) -{ - if(!parameter.name.empty()) - return get_name_from_words(parameter.name); - return get_name_from_words(parameter.kind); -} - -std::string Generator::get_member_name_from_enumerant( - const ast::Operand_kinds::Operand_kind::Enumerants::Enumerant &enumerant) -{ - return get_name_from_words(enumerant.enumerant + " parameters"); -} - -std::string Generator::get_operand_with_parameters_name( - Generator_state &state, const ast::Operand_kinds::Operand_kind &operand_kind) -{ - if(get_operand_has_any_parameters(state, operand_kind)) - return operand_kind.kind + "_with_parameters"; - return operand_kind.kind; -} - -void Generator::write_struct_nonstatic_members_and_constructors(Generator_state &state, - const std::string &struct_name, - const std::string *member_types, - const std::string *member_names, - std::size_t member_count) -{ - for(std::size_t i = 0; i < member_count; i++) - state << indent << member_types[i] << " " << member_names[i] << ";\n"; - state << indent << struct_name << "()\n"; - { - auto push_indent = state.pushed_indent(); - for(std::size_t i = 0; i < member_count; i++) + template + Generated_output_stream &operator<<(T) = delete; + Generated_output_stream &operator<<(char ch) { - state << indent; - if(i == 0) - state << ": "; - else - state << " "; - state << member_names[i] << "()"; - if(i != member_count - 1) - state << ","; - state << "\n"; + value.push_back(ch); + return *this; } - } - state << indent(R"({ -} -)"); - if(member_count != 0) - { - state << indent; - if(member_count == 1) - state << "explicit "; - state << struct_name << "("; - for(std::size_t i = 0; i < member_count; i++) + 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) { - state << member_types[i] << " " << member_names[i]; - if(i != member_count - 1) - state << ", "; + write_signed_integer(v); + return *this; } - state << ")\n"; + Generated_output_stream &operator<<(const ast::Copyright &v) { - auto push_indent = state.pushed_indent(); - for(std::size_t i = 0; i < member_count; i++) + *this << "/*\n"; + for(auto &line : v.lines) { - state << indent; - if(i == 0) - state << ": "; - else - state << " "; - state << member_names[i] << "(std::move(" << member_names[i] << "))"; - if(i != member_count - 1) - state << ","; - state << "\n"; + 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; } - state << indent(R"({ -} -)"); - } -} - -std::vector - Generator::get_unique_enumerants( - std::vector enumerants) -{ - std::unordered_set values; - std::size_t output_index = 0; - for(std::size_t input_index = 0; input_index < enumerants.size(); input_index++) + }; + struct Literal_holder { - if(std::get<1>(values.insert(enumerants[input_index].value))) + util::string_view value; + friend Generated_output_stream &operator<<(Generated_output_stream &os, + const Literal_holder &v) { - if(output_index != input_index) - enumerants[output_index] = std::move(enumerants[input_index]); - output_index++; + os.write_literal(v.value); + return os; } - } - enumerants.erase(enumerants.begin() + output_index, enumerants.end()); - return enumerants; -} - -struct Spirv_header_generator final : public Generator -{ - Spirv_header_generator() : Generator("spirv.h") + }; + static Literal_holder literal(util::string_view value) { + return Literal_holder{value}; } - enum class Enum_priority + struct Unsigned_integer_holder { - default_priority = 0, - capability = 1, + 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 Enum_priority get_enum_priority(const std::string &enum_name) noexcept - { - if(enum_name == capability_enum_name) - return Enum_priority::capability; - return Enum_priority::default_priority; - } - static bool compare_enum_names(const std::string &l, const std::string &r) noexcept + static Unsigned_integer_holder unsigned_integer( + std::uint64_t value, + unsigned base = json::ast::Number_value::default_base, + std::size_t min_length = 1) { - auto l_priority = get_enum_priority(l); - auto r_priority = get_enum_priority(r); - if(l_priority > r_priority) - return true; // higher priority sorts first - if(l_priority < r_priority) - return false; - return l < r; + return Unsigned_integer_holder{value, base, min_length}; } - /** lower priority means that the operand kind is declared first */ - static int get_operand_category_priority( - ast::Operand_kinds::Operand_kind::Category category) noexcept + struct Signed_integer_holder { - switch(category) + std::int64_t value; + unsigned base; + friend Generated_output_stream &operator<<(Generated_output_stream &os, + const Signed_integer_holder &v) { - case ast::Operand_kinds::Operand_kind::Category::bit_enum: - case ast::Operand_kinds::Operand_kind::Category::value_enum: - return 1; - case ast::Operand_kinds::Operand_kind::Category::id: - return 0; - case ast::Operand_kinds::Operand_kind::Category::literal: - return 0; - case ast::Operand_kinds::Operand_kind::Category::composite: - return 2; + os.write_unsigned_integer(v.value, v.base); + return os; } - return 0; - } - static bool compare_operand_kinds(const ast::Operand_kinds::Operand_kind &l, - const ast::Operand_kinds::Operand_kind &r) + }; + static Signed_integer_holder signed_integer( + std::int64_t value, unsigned base = json::ast::Number_value::default_base) { - auto l_priority = get_operand_category_priority(l.category); - auto r_priority = get_operand_category_priority(r.category); - if(l_priority != r_priority) - return l_priority < r_priority; - return compare_enum_names(l.kind, r.kind); + return Signed_integer_holder{value, base}; } - virtual void run(Generator_args &generator_args, const ast::Top_level &top_level) const override + class Word_iterator { - Generator_state state(this, generator_args, top_level); - state.open_output_file(); - write_file_comments(state, top_level.copyright); - write_file_guard_start(state); - state << indent(R"(#include -#include "util/enum.h" -#include "util/optional.h" -#include "util/variant.h" -#include + 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; -)"); - write_namespaces_start(state, spirv_namespace_names); - state << indent(R"(typedef std::uint32_t Word; -typedef Word Id; -enum class Op : Word; -constexpr Word magic_number = )") - << unsigned_hex_integer_literal(top_level.magic_number, 8) - << indent(true, - ";\n" - "constexpr std::uint32_t major_version = ") - << unsigned_dec_integer_literal(top_level.major_version) - << indent(true, - ";\n" - "constexpr std::uint32_t minor_version = ") - << unsigned_dec_integer_literal(top_level.minor_version) - << indent(true, - ";\n" - "constexpr std::uint32_t revision = ") - << unsigned_dec_integer_literal(top_level.revision) << ";\n"; - auto extensions_set = get_extensions(top_level); - std::vector extensions_list; - extensions_list.reserve(extensions_set.size()); - for(auto &extension : extensions_set) - extensions_list.push_back(extension); - std::sort(extensions_list.begin(), extensions_list.end()); - state << indent( - "\n" - "enum class ") - << extension_enum_name << indent(true, - " : std::size_t\n" - "{\n"); + private: + enum class Char_class { - auto push_indent = state.pushed_indent(); - for(auto &extension : extensions_list) - state << indent << get_enumerant_name(extension_enum_name, extension, false) - << ",\n"; - } - state << indent( - "};\n" - "\n" - "vulkan_cpu_util_generate_enum_traits(") - << extension_enum_name; - { - auto push_indent = state.pushed_indent(); - for(auto &extension : extensions_list) - state << ",\n" << indent << extension_enum_name - << "::" << get_enumerant_name(extension_enum_name, extension, false); - state << ");\n"; - } - state << indent( - "\n" - "constexpr const char *get_enumerant_name(") - << extension_enum_name << indent(true, - " v) noexcept\n" - "{\n"); + uppercase, + other_identifier, + word_separator + }; + static constexpr Char_class get_char_class(char ch) noexcept { - auto push_indent = state.pushed_indent(); - state << indent( - "switch(v)\n" - "{\n"); - for(auto &extension : extensions_list) - { - state << indent("case ") << extension_enum_name - << "::" << get_enumerant_name(extension_enum_name, extension, false) - << indent(true, - ":\n" - "`return \"") - << extension << "\";\n"; - } - state << indent( - "}\n" - "return \"\";\n"); + 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; } - state << "}\n"; - std::vector operand_kinds; - operand_kinds.reserve(top_level.operand_kinds.operand_kinds.size()); - for(auto &operand_kind : top_level.operand_kinds.operand_kinds) - operand_kinds.push_back(&operand_kind); - std::sort( - operand_kinds.begin(), - operand_kinds.end(), - [](const ast::Operand_kinds::Operand_kind *a, const ast::Operand_kinds::Operand_kind *b) - { - return compare_operand_kinds(*a, *b); - }); - for(auto *operand_kind : operand_kinds) + + private: + util::string_view word; + util::string_view words; + + private: + constexpr void next() noexcept { - switch(operand_kind->category) - { - case ast::Operand_kinds::Operand_kind::Category::bit_enum: - case ast::Operand_kinds::Operand_kind::Category::value_enum: + util::optional word_start; + Char_class last_char_class = Char_class::word_separator; + for(std::size_t i = 0; i < words.size(); i++) { - bool is_bit_enum = - operand_kind->category == ast::Operand_kinds::Operand_kind::Category::bit_enum; - auto &enumerants = - util::get(operand_kind->value); - auto unique_enumerants = get_unique_enumerants(enumerants.enumerants); - state << "\n" - "enum class " - << get_enum_name(*operand_kind) << " : Word\n" - "{\n"; - { - auto push_indent = state.pushed_indent(); - for(auto &enumerant : enumerants.enumerants) - { - state << indent - << get_enumerant_name(operand_kind->kind, enumerant.enumerant, false) - << " = "; - if(is_bit_enum) - state << unsigned_hex_integer_literal(enumerant.value); - else - state << unsigned_dec_integer_literal(enumerant.value); - state << ",\n"; - } - } - state << "};\n" - "\n" - "vulkan_cpu_util_generate_enum_traits(" - << get_enum_name(*operand_kind); - { - auto push_indent = state.pushed_indent(); - for(auto &enumerant : unique_enumerants) - state << ",\n" << indent << get_enum_name(*operand_kind) << "::" - << get_enumerant_name(operand_kind->kind, enumerant.enumerant, false); - state << ");\n"; - } - state << "\n" - "constexpr const char *get_enumerant_name(" - << get_enum_name(*operand_kind) << " v) noexcept\n" - "{\n"; - { - auto push_indent = state.pushed_indent(); - state << indent( - "switch(v)\n" - "{\n"); - for(auto &enumerant : unique_enumerants) - { - state << indent("case ") << get_enum_name(*operand_kind) << "::" - << get_enumerant_name(operand_kind->kind, enumerant.enumerant, false) - << indent(true, - ":\n" - "`return \"") - << enumerant.enumerant << "\";\n"; - } - state << indent( - "}\n" - "return \"\";\n"); - } - state << "}\n"; - state << "\n" - "constexpr util::Enum_set<" - << capability_enum_name << "> get_directly_required_capability_set(" - << get_enum_name(*operand_kind) << " v) noexcept\n" - "{\n"; - { - auto push_indent = state.pushed_indent(); - state << indent( - "switch(v)\n" - "{\n"); - for(auto &enumerant : unique_enumerants) - { - state << indent("case ") << get_enum_name(*operand_kind) << "::" - << get_enumerant_name(operand_kind->kind, enumerant.enumerant, false) - << indent(true, - ":\n" - "`return ") - << enumerant.capabilities << ";\n"; - } - state << indent( - "}\n" - "return {};\n"); - } - state << "}\n" - "\n" - "constexpr util::Enum_set<" - << extension_enum_name << "> get_directly_required_extension_set(" - << get_enum_name(*operand_kind) << " v) noexcept\n" - "{\n"; + auto current_char_class = get_char_class(words[i]); + if(word_start) { - auto push_indent = state.pushed_indent(); - state << indent( - "switch(v)\n" - "{\n"); - for(auto &enumerant : unique_enumerants) + switch(current_char_class) { - state << indent("case ") << get_enum_name(*operand_kind) << "::" - << get_enumerant_name(operand_kind->kind, enumerant.enumerant, false) - << indent(true, - ":\n" - "`return ") - << enumerant.extensions << ";\n"; + 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; } - state << indent( - "}\n" - "return {};\n"); - } - state << "}\n"; - break; - } - case ast::Operand_kinds::Operand_kind::Category::composite: - { - auto &bases = - util::get(operand_kind->value); - state << "\n" - "struct " - << operand_kind->kind << "\n" - "{\n"; - auto push_indent = state.pushed_indent(); - std::vector member_types; - std::vector member_names; - member_types.reserve(bases.values.size()); - member_names.reserve(bases.values.size()); - for(std::size_t i = 0; i < bases.values.size(); i++) - { - member_types.push_back( - get_operand_with_parameters_name(state, bases.values[i])); - member_names.push_back( - json::ast::Number_value::append_unsigned_integer_to_string(i + 1, "part_")); } - write_struct_nonstatic_members_and_constructors(state, - operand_kind->kind, - member_types.data(), - member_names.data(), - bases.values.size()); - push_indent.finish(); - state << "};\n"; - break; - } - case ast::Operand_kinds::Operand_kind::Category::id: - { - auto &doc = util::get(operand_kind->value); - state << "\n" - "/** "; - bool was_last_star = false; - for(char ch : doc.value) + else if(current_char_class != Char_class::word_separator) { - if(was_last_star && ch == '/') - state << ' '; - was_last_star = (ch == '*'); - state << ch; + word_start = i; } - state << " */\n" - "typedef Id " - << operand_kind->kind << ";\n"; - break; - } - case ast::Operand_kinds::Operand_kind::Category::literal: - { - auto &doc = util::get(operand_kind->value); - auto literal_kind = - ast::Operand_kinds::Operand_kind::get_literal_kind_from_json_name( - operand_kind->kind); - if(!literal_kind) - throw Generate_error("bad literal kind"); - auto base_type = "std::vector"; - switch(*literal_kind) - { - case ast::Operand_kinds::Operand_kind::Literal_kind::literal_integer: - // TODO: fix after determining if LiteralInteger can be multiple words - base_type = "std::uint32_t"; - break; - case ast::Operand_kinds::Operand_kind::Literal_kind::literal_string: - base_type = "std::string"; - break; - case ast::Operand_kinds::Operand_kind::Literal_kind:: - literal_context_dependent_number: - break; - case ast::Operand_kinds::Operand_kind::Literal_kind::literal_ext_inst_integer: - base_type = "Word"; - break; - case ast::Operand_kinds::Operand_kind::Literal_kind:: - literal_spec_constant_op_integer: - base_type = "Op"; - break; - } - state << "\n" - "/** "; - bool was_last_star = false; - for(char ch : doc.value) - { - if(was_last_star && ch == '/') - state << ' '; - was_last_star = (ch == '*'); - state << ch; - } - state << " */\n" - "typedef " - << base_type << " " << operand_kind->kind << ";\n"; - break; - } + last_char_class = current_char_class; } + if(word_start) + word = util::string_view(words.data() + *word_start, words.size() - *word_start); + else + word = {}; + words = {}; } - for(auto *operand_kind : operand_kinds) + constexpr bool at_end() const noexcept { - switch(operand_kind->category) - { - case ast::Operand_kinds::Operand_kind::Category::bit_enum: - case ast::Operand_kinds::Operand_kind::Category::value_enum: - { - bool is_bit_enum = - operand_kind->category == ast::Operand_kinds::Operand_kind::Category::bit_enum; - auto &enumerants = - util::get(operand_kind->value); - auto unique_enumerants = get_unique_enumerants(enumerants.enumerants); - if(get_operand_has_any_parameters(state, *operand_kind)) - { - for(auto &enumerant : unique_enumerants) - { - if(enumerant.parameters.empty()) - continue; - auto struct_name = get_enumerant_parameters_struct_name( - operand_kind->kind, enumerant.enumerant, false); - state << "\n" - "struct " - << struct_name << indent(true, R"( -{ -`static constexpr )") << get_enum_name(*operand_kind) - << indent(true, R"( get_enumerant() noexcept -`{ -``return )") << get_enum_name(*operand_kind) - << "::" - << get_enumerant_name(operand_kind->kind, enumerant.enumerant, false) - << indent(true, R"(; -`} -)"); - std::vector member_types; - std::vector member_names; - member_types.reserve(enumerant.parameters.parameters.size()); - member_names.reserve(enumerant.parameters.parameters.size()); - for(auto ¶meter : enumerant.parameters.parameters) - { - member_types.push_back( - get_operand_with_parameters_name(state, parameter.kind)); - member_names.push_back(get_member_name_from_parameter(parameter)); - } - auto push_indent = state.pushed_indent(); - write_struct_nonstatic_members_and_constructors( - state, - struct_name, - member_types.data(), - member_names.data(), - enumerant.parameters.parameters.size()); - push_indent.finish(); - state << "};\n"; - } - if(is_bit_enum) - { - auto struct_name = get_operand_with_parameters_name(state, *operand_kind); - state << indent(R"( -struct )") << struct_name << indent(true, R"( -{ -)"); - std::vector member_types; - std::vector member_names; - member_types.push_back(get_enum_name(*operand_kind)); - member_names.push_back("value"); - for(auto &enumerant : unique_enumerants) - { - if(enumerant.parameters.empty()) - continue; - member_types.push_back( - "util::optional<" - + get_enumerant_parameters_struct_name( - operand_kind->kind, enumerant.enumerant, false) - + ">"); - member_names.push_back(get_member_name_from_enumerant(enumerant)); - } - auto push_indent = state.pushed_indent(); - write_struct_nonstatic_members_and_constructors(state, - struct_name, - member_types.data(), - member_names.data(), - member_types.size()); - push_indent.finish(); - state << "};\n"; - } - else - { - auto struct_name = get_operand_with_parameters_name(state, *operand_kind); - state << indent(R"( -struct )") << struct_name << indent(true, R"( -{ -`typedef util::variantkind, enumerant.enumerant, false); - } - state << indent(true, R"(> Parameters; -)"); - auto push_indent = state.pushed_indent(); - constexpr std::size_t member_count = 2; - std::string member_types[member_count] = { - get_enum_name(*operand_kind), "Parameters", - }; - std::string member_names[member_count] = { - "value", "parameters", - }; - write_struct_nonstatic_members_and_constructors( - state, struct_name, member_types, member_names, member_count); - push_indent.finish(); - state << "};\n"; - } - } - break; - } - case ast::Operand_kinds::Operand_kind::Category::composite: - case ast::Operand_kinds::Operand_kind::Category::id: - case ast::Operand_kinds::Operand_kind::Category::literal: - break; - } + return word.empty(); } - std::vector instructions; - instructions.reserve(top_level.instructions.instructions.size()); - for(auto &instruction : top_level.instructions.instructions) - instructions.push_back(&instruction); - std::sort( - instructions.begin(), - instructions.end(), - [](const ast::Instructions::Instruction *a, const ast::Instructions::Instruction *b) - { - return a->opcode < b->opcode; - }); - state << "\n" - "enum class " - << op_enum_name << " : Word\n" - "{\n"; + + public: + constexpr Word_iterator() noexcept : word(), words() { - auto push_indent = state.pushed_indent(); - for(auto &instruction : top_level.instructions.instructions) - { - state << indent << get_enumerant_name(op_enum_name, instruction.opname, true) - << " = " << unsigned_dec_integer_literal(instruction.opcode) << ",\n"; - } } - state << "};\n"; - state << "\n" - "vulkan_cpu_util_generate_enum_traits(" - << op_enum_name; + constexpr explicit Word_iterator(util::string_view words) noexcept : word(), words(words) { - auto push_indent = state.pushed_indent(); - for(auto &instruction : top_level.instructions.instructions) - state << ",\n" << indent << op_enum_name - << "::" << get_enumerant_name(op_enum_name, instruction.opname, true); - state << ");\n"; + next(); } - state << "\n" - "constexpr const char *get_enumerant_name(" - << op_enum_name << " v) noexcept\n" - "{\n"; + constexpr const util::string_view &operator*() const noexcept { - auto push_indent = state.pushed_indent(); - state << indent( - "switch(v)\n" - "{\n"); - for(auto &instruction : top_level.instructions.instructions) - { - state << indent("case ") << op_enum_name - << "::" << get_enumerant_name(op_enum_name, instruction.opname, true) - << indent(true, - ":\n" - "return \"") - << instruction.opname << "\";\n"; - } - state << indent( - "}\n" - "return \"\";\n"); + return word; } - state << "}\n" - "\n" - "constexpr util::Enum_set<" - << capability_enum_name << "> get_directly_required_capability_set(" << op_enum_name - << " v) noexcept\n" - "{\n"; + constexpr const util::string_view *operator->() const noexcept { - auto push_indent = state.pushed_indent(); - state << indent( - "switch(v)\n" - "{\n"); - for(auto &instruction : top_level.instructions.instructions) - { - state << indent("case ") << op_enum_name - << "::" << get_enumerant_name(op_enum_name, instruction.opname, true) - << indent(true, - ":\n" - "return ") - << instruction.capabilities << ";\n"; - } - state << indent( - "}\n" - "return {};\n"); + return &word; } - state << "}\n" - "\n" - "constexpr util::Enum_set<" - << extension_enum_name << "> get_directly_required_extension_set(" << op_enum_name - << " v) noexcept\n" - "{\n"; + constexpr Word_iterator &operator++() noexcept { - auto push_indent = state.pushed_indent(); - state << indent( - "switch(v)\n" - "{\n"); - for(auto &instruction : top_level.instructions.instructions) - { - state << indent("case ") << op_enum_name - << "::" << get_enumerant_name(op_enum_name, instruction.opname, true) - << ":\n"; - auto push_indent2 = state.pushed_indent(); - state << indent("return ") << instruction.extensions << ";\n"; - } - state << indent( - "}\n" - "return {};\n"); + next(); + return *this; } - state << "}\n"; - for(auto &instruction : top_level.instructions.instructions) + constexpr Word_iterator operator++(int) noexcept { - auto struct_name = get_enumerant_name(op_enum_name, instruction.opname, true); - state << "\n" - "struct " - << struct_name << "\n" - "{\n"; - { - auto push_indent = state.pushed_indent(); - state << indent("static constexpr ") << op_enum_name << " get_opcode() noexcept\n" - << indent("{\n"); - { - auto push_indent2 = state.pushed_indent(); - state << indent("return ") << op_enum_name - << "::" << get_enumerant_name(op_enum_name, instruction.opname, true) - << ";\n"; - } - state << indent("}\n"); - std::vector member_names; - std::vector member_types; - member_names.reserve(instruction.operands.operands.size()); - member_types.reserve(instruction.operands.operands.size()); - for(auto &operand : instruction.operands.operands) - { - std::string member_type; - switch(operand.quantifier) - { - case ast::Instructions::Instruction::Operands::Operand::Quantifier::none: - { - member_type = get_operand_with_parameters_name(state, operand); - break; - } - case ast::Instructions::Instruction::Operands::Operand::Quantifier::optional: - { - member_type = "util::optional<" - + get_operand_with_parameters_name(state, operand) + ">"; - break; - } - case ast::Instructions::Instruction::Operands::Operand::Quantifier::variable: - { - member_type = - "std::vector<" + get_operand_with_parameters_name(state, operand) + ">"; - break; - } - } - member_types.push_back(std::move(member_type)); - member_names.push_back(get_member_name_from_operand(operand)); - } - write_struct_nonstatic_members_and_constructors(state, - struct_name, - member_types.data(), - member_names.data(), - member_types.size()); - } - state << "};\n"; + auto retval = *this; + next(); + return retval; } - write_namespaces_end(state, spirv_namespace_names); - write_file_guard_end(state); - } -}; - -struct Spirv_source_generator final : public Generator -{ - Spirv_source_generator() : Generator("spirv.cpp") - { - } - virtual void run(Generator_args &generator_args, const ast::Top_level &top_level) const override - { - Generator_state state(this, generator_args, top_level); - state.open_output_file(); - write_file_comments(state, top_level.copyright); - state << "#include \"spirv.h\"\n"; - } -}; - -struct Parser_header_generator final : public Generator -{ - Parser_header_generator() : Generator("parser.h") - { - } - static std::string get_dump_operand_function_name(std::string kind) - { - return "dump_operand_" + std::move(kind); - } - static std::string get_dump_operand_function_name( - const ast::Operand_kinds::Operand_kind &operand_kind) - { - return get_dump_operand_function_name(operand_kind.kind); - } - static std::string get_dump_operand_function_name( - const ast::Operand_kinds::Operand_kind::Enumerants::Enumerant::Parameters::Parameter - ¶meter) - { - return get_dump_operand_function_name(parameter.kind); - } - static std::string get_parse_operand_function_name(std::string kind) - { - return "parse_operand_" + std::move(kind); - } - static std::string get_parse_operand_function_name( - const ast::Operand_kinds::Operand_kind &operand_kind) - { - return get_parse_operand_function_name(operand_kind.kind); - } - static std::string get_parse_operand_function_name( - const ast::Instructions::Instruction::Operands::Operand &operand) - { - return get_parse_operand_function_name(operand.kind); - } - static std::string get_parse_operand_function_name( - const ast::Operand_kinds::Operand_kind::Enumerants::Enumerant::Parameters::Parameter - ¶meter) - { - return get_parse_operand_function_name(parameter.kind); - } - static std::string get_parse_instruction_function_name(std::string opname) - { - return "parse_instruction_" + get_enumerant_name(op_enum_name, opname, true); - } - virtual void run(Generator_args &generator_args, const ast::Top_level &top_level) const override - { - Generator_state state(this, generator_args, top_level); - state.open_output_file(); - write_file_comments(state, top_level.copyright); - write_file_guard_start(state); - state << R"(#include "spirv.h" -#include -#include -#include "util/optional.h" -#include "json/json.h" -#include - -)"; - write_namespaces_start(state, spirv_namespace_names); - state << indent(R"(struct Parse_error -{ -`std::size_t word_index; -`std::size_t instruction_word_index; -`std::string message; -`Parse_error(std::size_t word_index, std::size_t instruction_word_index, std::string message) noexcept -``: word_index(word_index), -`` instruction_word_index(instruction_word_index), -`` message(std::move(message)) -`{ -`} -`virtual ~Parse_error() = default; -}; - -)"); - state << "struct Parse_semantics_generic\n" - "{\n"; + constexpr bool operator==(const Word_iterator &rt) const noexcept { - auto push_indent = state.pushed_indent(); - state << indent(R"(virtual ~Parse_semantics_generic() = default; -virtual std::unique_ptr handle_error(std::size_t word_index, std::size_t instruction_word_index, std::string message) = 0; -virtual void handle_spirv_version(unsigned major, unsigned minor) = 0; -virtual void handle_generator_magic_number(Word value) = 0; -virtual void handle_id_bound(Word id_bound) = 0; -)"); - for(auto &instruction : top_level.instructions.instructions) - { - auto struct_name = get_enumerant_name(op_enum_name, instruction.opname, true); - state << indent("virtual void handle_instruction(") << struct_name - << " instruction) = 0;\n"; - } + return word.empty() == rt.word.empty(); } - state << indent(R"(}; - -struct Parse_dump final : public Parse_semantics_generic -{ -`std::ostream &os; -`explicit constexpr Parse_dump(std::ostream &os) noexcept : os(os) -`{ -`} -)"); + constexpr bool operator!=(const Word_iterator &rt) const noexcept { - auto push_indent = state.pushed_indent(); - state << indent( - R"(virtual std::unique_ptr handle_error(std::size_t word_index, std::size_t instruction_word_index, std::string message) override; -virtual void handle_spirv_version(unsigned major, unsigned minor) override; -virtual void handle_generator_magic_number(Word value) override; -virtual void handle_id_bound(Word id_bound) override; -)"); - for(auto &instruction : top_level.instructions.instructions) - { - auto struct_name = get_enumerant_name(op_enum_name, instruction.opname, true); - state << indent("virtual void handle_instruction(") << struct_name - << " instruction) override;\n"; - } - for(auto &operand_kind : top_level.operand_kinds.operand_kinds) - { - auto dump_function_name = get_dump_operand_function_name(operand_kind); - state << indent("void ") << dump_function_name << "(const " - << get_operand_with_parameters_name(state, operand_kind) << " &value);\n"; - state << indent("void ") << dump_function_name << "(const util::optional<" - << get_operand_with_parameters_name(state, operand_kind) << "> &value);\n"; - state << indent("void ") << dump_function_name << "(const std::vector<" - << get_operand_with_parameters_name(state, operand_kind) << "> &values);\n"; - } + return word.empty() != rt.word.empty(); } - state << "};\n" - "\n" - "template \n" - "struct Parser\n" - "{\n"; + constexpr Word_iterator begin() const noexcept { - auto push_indent = state.pushed_indent(); - for(auto &operand_kind : top_level.operand_kinds.operand_kinds) - { - auto parse_function_name = get_parse_operand_function_name(operand_kind); - state - << indent(R"(static std::unique_ptr )") << parse_function_name - << indent( - true, - R"((const Word *words, std::size_t word_count, Semantics &semantics, std::size_t error_instruction_index, std::size_t &word_index, )") - << get_operand_with_parameters_name(state, operand_kind) << indent(true, - R"( &value) -{ -`if(word_index >= word_count) -``return semantics.handle_error(error_instruction_index + word_index, error_instruction_index, "instruction missing operand"); -)"); - auto push_indent = state.pushed_indent(); - switch(operand_kind.category) - { - case ast::Operand_kinds::Operand_kind::Category::bit_enum: - { - auto enum_name = get_enum_name(operand_kind); - if(get_operand_has_any_parameters(state, operand_kind)) - { - state << indent(R"(value.value = static_cast<)") << enum_name - << indent(true, R"(>(words[word_index++]); -)"); - auto &enumerants = util::get( - operand_kind.value); - for(auto &enumerant : enumerants.enumerants) - { - if(enumerant.parameters.empty()) - continue; - auto enumerant_member_name = get_member_name_from_enumerant(enumerant); - auto enumerant_name = - get_enumerant_name(operand_kind.kind, enumerant.enumerant, false); - state - << indent( - R"(if((static_cast(value.value) & static_cast()") - << enum_name << "::" << enumerant_name - << indent(true, R"()) == static_cast()") << enum_name - << "::" << enumerant_name << indent(true, R"()) -{ -`value.)") << enumerant_member_name - << indent(true, R"(.emplace(); -)"); - bool first = true; - for(auto ¶meter : enumerant.parameters.parameters) - { - auto parameter_member_name = - get_member_name_from_parameter(parameter); - auto parameter_parse_function = - get_parse_operand_function_name(parameter); - state << indent(1); - if(first) - { - state << indent(true, "auto "); - first = false; - } - state << indent(true, "parse_error = ") << parameter_parse_function - << "(words, word_count, semantics, error_instruction_index, " - "word_index, value." - << enumerant_member_name << "->" << parameter_member_name - << indent(true, R"(); -`if(parse_error) -``return parse_error; -)"); - } - state << indent(R"(} -)"); - } - } - else - { - state << indent(R"(value = static_cast<)") << enum_name - << indent(true, R"(>(words[word_index++]); -)"); - } - break; - } - case ast::Operand_kinds::Operand_kind::Category::value_enum: - { - auto enum_name = get_enum_name(operand_kind); - if(get_operand_has_any_parameters(state, operand_kind)) - { - state << indent(R"(value.value = static_cast<)") << enum_name - << indent(true, R"(>(words[word_index++]); -switch(value.value) -{ -)"); - auto &enumerants = util::get( - operand_kind.value); - for(auto &enumerant : enumerants.enumerants) - { - auto enumerant_name = - get_enumerant_name(operand_kind.kind, enumerant.enumerant, false); - state << indent("case ") << enum_name << "::" << enumerant_name - << indent(true, R"(: -)"); - if(enumerant.parameters.empty()) - { - state << indent(R"(`break; -)"); - continue; - } - auto enumerant_parameters_struct_name = - get_enumerant_parameters_struct_name( - operand_kind.kind, enumerant.enumerant, false); - state << indent(R"({ -`value.parameters.emplace<)") << enumerant_parameters_struct_name - << indent(true, R"(>(); -`auto ¶meters = util::get<)") << enumerant_parameters_struct_name - << indent(true, R"(>(value.parameters); -)"); - bool first = true; - for(auto ¶meter : enumerant.parameters.parameters) - { - auto parameter_member_name = - get_member_name_from_parameter(parameter); - auto parameter_parse_function = - get_parse_operand_function_name(parameter); - state << indent(1); - if(first) - { - state << indent(true, "auto "); - first = false; - } - state << indent(true, "parse_error = ") << parameter_parse_function - << "(words, word_count, semantics, error_instruction_index, " - "word_index, parameters." - << parameter_member_name << indent(true, R"(); -`if(parse_error) -``return parse_error; -)"); - } - state << indent(R"(`break; -} -)"); - } - state << indent(R"(default: -`word_index--; -`return semantics.handle_error(error_instruction_index + word_index, error_instruction_index, "invalid enum value"); -} -)"); - } - else - { - state << indent(R"(value = static_cast<)") << enum_name - << indent(true, R"(>(words[word_index]); -if(util::Enum_traits<)") << enum_name - << indent(true, R"(>::find_value(value) == util::Enum_traits<)") - << enum_name << indent(true, R"(>::npos) -`return semantics.handle_error(error_instruction_index + word_index, error_instruction_index, "invalid enum value"); -word_index++; -)"); - } - break; - } - case ast::Operand_kinds::Operand_kind::Category::composite: - { - auto &bases = - util::get(operand_kind.value); - for(std::size_t i = 0; i < bases.values.size(); i++) - { - state << indent; - if(i == 0) - state << indent(true, "auto "); - state << indent(true, "parse_error = ") - << get_parse_operand_function_name(bases.values[i]) - << "(words, word_count, semantics, error_instruction_index, " - "word_index, value." - << json::ast::Number_value::append_unsigned_integer_to_string(i + 1, - "part_") - << indent(true, R"(); -if(parse_error) -`return parse_error; -)"); - } - break; - } - case ast::Operand_kinds::Operand_kind::Category::id: - { - state << indent(R"(value = static_cast(words[word_index++]); -)"); - break; - } - case ast::Operand_kinds::Operand_kind::Category::literal: - { - auto literal_kind = - ast::Operand_kinds::Operand_kind::get_literal_kind_from_json_name( - operand_kind.kind); - if(!literal_kind) - throw Generate_error("bad literal kind"); - switch(*literal_kind) - { - case ast::Operand_kinds::Operand_kind::Literal_kind::literal_integer: - // TODO: fix after determining if LiteralInteger can be multiple words - state << indent(R"(value = words[word_index++]; -)"); - break; - case ast::Operand_kinds::Operand_kind::Literal_kind::literal_string: - state << indent( - R"(value.clear(); -bool done = false; -while(!done) -{ -`if(word_index >= word_count) -``return semantics.handle_error(error_instruction_index + word_index, error_instruction_index, "string missing terminating null"); -`Word word = words[word_index++]; -`for(std::size_t i = 0; i < 4; i++) -`{ -``unsigned char ch = word & 0xFFU; -``word >>= 8; -``if(ch == '\0') -``{ -```done = true; -```if(word != 0) -````return semantics.handle_error(error_instruction_index + word_index, error_instruction_index, "string has non-zero padding"); -``} -``else -``{ -```value += ch; -``} -`} -} -)"); - break; - case ast::Operand_kinds::Operand_kind::Literal_kind:: - literal_context_dependent_number: - state << indent( - R"(static_assert(std::is_same &>::value, "missing parse code for operand kind"); -value.clear(); -value.reserve(word_count - word_index); -while(word_index < word_count) -`value.push_back(words[word_index++]); -)"); - break; - case ast::Operand_kinds::Operand_kind::Literal_kind::literal_ext_inst_integer: - state << indent(R"(value = words[word_index++]; -)"); - break; - case ast::Operand_kinds::Operand_kind::Literal_kind:: - literal_spec_constant_op_integer: - state << indent(R"(value = static_cast<)") << op_enum_name - << indent(true, R"(>(words[word_index++]); -)"); - break; - } - break; - } - } - push_indent.finish(); - state << indent(R"(`return nullptr; -} -)"); - } - for(auto &instruction : top_level.instructions.instructions) - { - auto struct_name = get_enumerant_name(op_enum_name, instruction.opname, true); - auto parse_function_name = get_parse_instruction_function_name(instruction.opname); - state - << indent(R"(static std::unique_ptr )") << parse_function_name - << indent( - true, - R"((const Word *words, std::size_t word_count, Semantics &semantics, std::size_t error_instruction_index) -{ -`std::size_t word_index = 1; // skip opcode -)"); - auto push_indent2 = state.pushed_indent(); - state << indent << struct_name << " instruction;\n"; - if(!instruction.operands.empty()) - state << indent("std::unique_ptr parse_error;\n"); - for(auto &operand : instruction.operands.operands) - { - auto parse_operand_function_name = get_parse_operand_function_name(operand); - auto member_name = get_member_name_from_operand(operand); - switch(operand.quantifier) - { - case ast::Instructions::Instruction::Operands::Operand::Quantifier::none: - { - state - << indent(R"(parse_error = )") << parse_operand_function_name - << indent( - true, - R"((words, word_count, semantics, error_instruction_index, word_index, instruction.)") - << member_name << indent(true, R"(); -if(parse_error) -`return parse_error; -)"); - break; - } - case ast::Instructions::Instruction::Operands::Operand::Quantifier::optional: - { - state - << indent(R"(if(word_index < word_count) -{ -`instruction.)") << member_name - << indent(true, R"(.emplace(); -`parse_error = )") << parse_operand_function_name - << indent( - true, - R"((words, word_count, semantics, error_instruction_index, word_index, *instruction.)") - << member_name << indent(true, R"(); -`if(parse_error) -``return parse_error; -} -)"); - break; - } - case ast::Instructions::Instruction::Operands::Operand::Quantifier::variable: - { - state - << indent(R"(while(word_index < word_count) -{ -`instruction.)") << member_name - << indent(true, R"(.emplace_back(); -`parse_error = )") << parse_operand_function_name - << indent( - true, - R"((words, word_count, semantics, error_instruction_index, word_index, instruction.)") - << member_name << indent(true, R"(.back()); -`if(parse_error) -``return parse_error; -} -)"); - } - } - } - push_indent2.finish(); - state << indent(R"(`if(word_index < word_count) -``return semantics.handle_error(error_instruction_index + word_index, error_instruction_index, "extra words at end of instruction"); -`semantics.handle_instruction(std::move(instruction)); -`return nullptr; -} -)"); - } - state << indent( - R"(static std::unique_ptr parse_instruction(const Word *words, std::size_t word_count, Semantics &semantics, std::size_t error_instruction_index) -{ -`Op op = static_cast(words[0] & 0xFFFFU); -`switch(op) -`{ -)"); - for(auto &instruction : top_level.instructions.instructions) - { - auto push_indent2 = state.pushed_indent(2); - auto enumerant_name = get_enumerant_name(op_enum_name, instruction.opname, true); - auto parse_function_name = get_parse_instruction_function_name(instruction.opname); - state << indent("case ") << op_enum_name << "::" << enumerant_name - << indent(true, R"(: -`return )") << parse_function_name - << indent(true, R"((words, word_count, semantics, error_instruction_index); -)"); - } - state << indent(R"(`} -`return semantics.handle_error(error_instruction_index, error_instruction_index, json::ast::Number_value::append_unsigned_integer_to_string(static_cast(op), "unknown instruction: 0x", 0x10)); -} -static std::unique_ptr parse(const Word *words, std::size_t word_count, Semantics &semantics) -{ -`std::size_t word_index = 0; -`if(word_index >= word_count) -``return semantics.handle_error(word_index, 0, "hit EOF when parsing magic number"); -`if(words[word_index] != magic_number) -``return semantics.handle_error(word_index, 0, "invalid magic number"); -`word_index++; -`if(word_index >= word_count) -``return semantics.handle_error(word_index, 0, "hit EOF when parsing SPIR-V version"); -`if(words[word_index] & ~0xFFFF00UL) -``return semantics.handle_error(word_index, 0, "invalid SPIR-V version"); -`auto input_major_version = words[word_index] >> 16; -`auto input_minor_version = (words[word_index] >> 8) & 0xFFU; -`semantics.handle_spirv_version(input_major_version, input_minor_version); -`if(input_major_version != major_version || input_minor_version > minor_version) -``return semantics.handle_error(word_index, 0, "SPIR-V version not supported"); -`word_index++; -`if(word_index >= word_count) -``return semantics.handle_error(word_index, 0, "hit EOF when parsing generator's magic number"); -`semantics.handle_generator_magic_number(words[word_index++]); -`if(word_index >= word_count) -``return semantics.handle_error(word_index, 0, "hit EOF when parsing id bound"); -`semantics.handle_id_bound(words[word_index++]); -`if(word_index >= word_count) -``return semantics.handle_error(word_index, 0, "hit EOF when parsing SPIR-V shader header"); -`if(words[word_index] != 0) -``return semantics.handle_error(word_index, 0, "nonzero reserved word in SPIR-V shader header"); -`word_index++; -`// now we've finished reading the shader header, the rest of the shader is just instructions -`while(word_index < word_count) -`{ -``auto instruction_word_count = words[word_index] >> 16; -``if(instruction_word_count == 0) -```return semantics.handle_error(word_index, word_index, "invalid instruction"); -``if(word_index + instruction_word_count > word_count) -```return semantics.handle_error(word_index, word_index, "instruction longer than rest of shader"); -``auto parse_error = parse_instruction(words + word_index, instruction_word_count, semantics, word_index); -``if(parse_error) -```return parse_error; -``word_index += instruction_word_count; -`} -`return nullptr; -} -)"); + return *this; } - state << "};\n"; - write_namespaces_end(state, spirv_namespace_names); - write_file_guard_end(state); - } -}; - -struct Parser_source_generator final : public Generator -{ - Parser_source_generator() : Generator("parser.cpp") - { - } - virtual void run(Generator_args &generator_args, const ast::Top_level &top_level) const override + constexpr Word_iterator end() const noexcept + { + return {}; + } + }; + static void write_guard_macro(Generated_output_stream &os) { - Generator_state state(this, generator_args, top_level); - state.open_output_file(); - write_file_comments(state, top_level.copyright); - state << "#include \"parser.h\"\n" - "#include \n" - "\n"; - write_namespaces_start(state, spirv_namespace_names); - state << indent(R"(namespace -{ -/** instantiate Parser with Parse_semantics_generic to help catch bugs */ -[[gnu::unused]] auto parser_test(const Word *words, std::size_t word_count, Parse_semantics_generic &semantics) -{ -`return Parser<>::parse(words, word_count, semantics); -} -} - -std::unique_ptr Parse_dump::handle_error(std::size_t word_index, std::size_t instruction_word_index, std::string message) -{ -`return std::unique_ptr(new Parse_error(word_index, instruction_word_index, std::move(message))); -} - -void Parse_dump::handle_spirv_version(unsigned major, unsigned minor) -{ -`os << "SPIR-V version " << major << "." << minor << "\n"; -} - -void Parse_dump::handle_generator_magic_number(Word value) -{ -`os << "generator magic number: " << json::ast::Number_value::append_unsigned_integer_to_string(value, "0x", 0x10) << "\n"; -} - -void Parse_dump::handle_id_bound(Word id_bound) -{ -`os << "id bound: " << json::ast::Number_value::unsigned_integer_to_string(id_bound) << "\n"; -} -)"); - for(auto &operand_kind : top_level.operand_kinds.operand_kinds) + auto path_string = os.get_file_path().string(); + for(auto &word : Word_iterator(path_string)) { - auto dump_function_name = - Parser_header_generator::get_dump_operand_function_name(operand_kind); + for(char ch : word) { - state << indent(R"( -void Parse_dump::)") << dump_function_name - << "(const " << get_operand_with_parameters_name(state, operand_kind) - << R"( &value) -{ -)"; - auto push_indent = state.pushed_indent(); - switch(operand_kind.category) - { - case ast::Operand_kinds::Operand_kind::Category::bit_enum: - { - bool operand_has_any_parameters = - get_operand_has_any_parameters(state, operand_kind); - state << indent(R"(Word bits = static_cast(value)") - << (operand_has_any_parameters ? ".value" : "") << indent(true, R"(); -bool first = true; -)"); - auto enum_name = get_enum_name(operand_kind); - auto &enumerants = - util::get(operand_kind.value); - const ast::Operand_kinds::Operand_kind::Enumerants::Enumerant *zero_enumerant = - nullptr; - for(auto &enumerant : enumerants.enumerants) - { - if(enumerant.value == 0) - { - zero_enumerant = &enumerant; - continue; - } - auto enumerant_name = - get_enumerant_name(operand_kind.kind, enumerant.enumerant, false); - auto scoped_enumerant_name = enum_name + "::" + enumerant_name; - state << indent(R"(if((bits & static_cast()") << scoped_enumerant_name - << indent(true, R"()) == static_cast()") - << scoped_enumerant_name << indent(true, R"()) -{ -`bits &= ~static_cast()") - << scoped_enumerant_name << indent(true, R"(); -`if(first) -``first = false; -`else -``os << " | "; -`os << get_enumerant_name()") << scoped_enumerant_name - << indent(true, R"(); -)"); - if(!enumerant.parameters.empty()) - { - auto enumerant_member_name = get_member_name_from_enumerant(enumerant); - state << indent(R"(`os << "("; -`if(value.)") << enumerant_member_name - << indent(true, R"() -`{ -``auto ¶meters = *value.)") << enumerant_member_name - << indent(true, R"(; -)"); - bool first = true; - for(auto ¶meter : enumerant.parameters.parameters) - { - if(first) - first = false; - else - state << indent(R"(``os << ", "; -)"); - state << indent(2) - << Parser_header_generator::get_dump_operand_function_name( - parameter) - << "(parameters." << get_member_name_from_parameter(parameter) - << ");\n"; - } - state << indent(R"a(`} -`os << ")"; -)a"); - } - state << indent(R"(} -)"); - } - std::string zero_enumerant_name_code; - if(zero_enumerant) - { - zero_enumerant_name_code = - "get_enumerant_name(" + enum_name + "::" - + get_enumerant_name( - operand_kind.kind, zero_enumerant->enumerant, false) - + ")"; - } - else - { - zero_enumerant_name_code = "\"0\""; - } - state << indent(R"(if(bits != 0) -{ -`if(!first) -``os << " | "; -`os << json::ast::Number_value::append_unsigned_integer_to_string(bits, "0x", 0x10); -} -else if(first) -{ -`os << )") << zero_enumerant_name_code - << indent(true, R"(; -} -)"); - break; - } - case ast::Operand_kinds::Operand_kind::Category::value_enum: - { - auto enum_name = get_enum_name(operand_kind); - if(get_operand_has_any_parameters(state, operand_kind)) - { - state << indent(R"(switch(value.value) -{ -)"); - auto &enumerants = util::get( - operand_kind.value); - bool any_parameterless_enumerants = false; - for(auto &enumerant : enumerants.enumerants) - { - if(!enumerant.parameters.empty()) - continue; - any_parameterless_enumerants = true; - state << indent("case ") << enum_name - << "::" << get_enumerant_name( - operand_kind.kind, enumerant.enumerant, false) - << ":\n"; - } - if(any_parameterless_enumerants) - { - state << indent(R"(`os << get_enumerant_name(value.value); -`break; -)"); - } - for(auto &enumerant : enumerants.enumerants) - { - if(enumerant.parameters.empty()) - continue; - auto enumerant_name = - get_enumerant_name(operand_kind.kind, enumerant.enumerant, false); - auto scoped_enumerant_name = enum_name + "::" + enumerant_name; - auto enumerant_parameters_struct_name = - get_enumerant_parameters_struct_name( - operand_kind.kind, enumerant.enumerant, false); - state << indent("case ") << scoped_enumerant_name << indent(true, R"(: -{ -`os << get_enumerant_name()") << scoped_enumerant_name - << indent(true, R"() << "("; -`auto *parameters = util::get_if<)") - << enumerant_parameters_struct_name - << indent(true, R"(>(&value.parameters); -`if(parameters) -`{ -)"); - bool first = true; - for(auto ¶meter : enumerant.parameters.parameters) - { - if(first) - first = false; - else - state << indent(R"(``os << ", "; -)"); - state << indent(2) - << Parser_header_generator::get_dump_operand_function_name( - parameter) - << "(parameters->" - << get_member_name_from_parameter(parameter) << ");\n"; - } - state << indent(R"a(`} -`os << ")"; -`break; -} -)a"); - } - state << indent(R"(default: -`os << json::ast::Number_value::unsigned_integer_to_string(static_cast(value.value)); -} -)"); - } - else - { - state << indent(R"(if(util::Enum_traits<)") << enum_name - << indent(true, R"(>::find_value(value) == util::Enum_traits<)") - << enum_name << indent(true, R"(>::npos) -`os << json::ast::Number_value::unsigned_integer_to_string(static_cast(value)); -else -`os << get_enumerant_name(value); -)"); - } - break; - } - case ast::Operand_kinds::Operand_kind::Category::composite: - { - auto &bases = - util::get(operand_kind.value); - state << indent("os << \"{\";\n"); - for(std::size_t i = 0; i < bases.values.size(); i++) - { - if(i != 0) - { - state << indent("os << \", \";\n"); - } - state << indent << Parser_header_generator::get_dump_operand_function_name( - bases.values[i]) - << "(value." - << json::ast::Number_value::append_unsigned_integer_to_string(i + 1, - "part_") - << ");\n"; - } - state << indent("os << \"}\";\n"); - break; - } - case ast::Operand_kinds::Operand_kind::Category::id: - { - state << indent( - R"(os << json::ast::Number_value::append_unsigned_integer_to_string(value, "#"); -)"); - break; - } - case ast::Operand_kinds::Operand_kind::Category::literal: - { - auto literal_kind = - ast::Operand_kinds::Operand_kind::get_literal_kind_from_json_name( - operand_kind.kind); - if(!literal_kind) - throw Generate_error("bad literal kind"); - switch(*literal_kind) - { - case ast::Operand_kinds::Operand_kind::Literal_kind::literal_integer: - state << indent( - R"(os << json::ast::Number_value::append_unsigned_integer_to_string(value, "0x"); -)"); - break; - case ast::Operand_kinds::Operand_kind::Literal_kind::literal_string: - state << indent( - R"(json::ast::String_value::write(os, value); -)"); - break; - case ast::Operand_kinds::Operand_kind::Literal_kind:: - literal_context_dependent_number: - state << indent( - R"(static_assert(std::is_same &>::value, "missing dump code for operand kind"); -auto separator = ""; -os << "{"; -for(Word word : value) -{ -`os << separator; -`separator = ", "; -`os << json::ast::Number_value::append_unsigned_integer_to_string(word, "0x", 0x10, 8); -} -os << "}"; -)"); - break; - case ast::Operand_kinds::Operand_kind::Literal_kind::literal_ext_inst_integer: - state << indent( - R"(os << json::ast::Number_value::append_unsigned_integer_to_string(value, "0x"); -)"); - break; - case ast::Operand_kinds::Operand_kind::Literal_kind:: - literal_spec_constant_op_integer: - state << indent(R"(if(util::Enum_traits<)") << op_enum_name - << indent(true, R"(>::find_value(value) == util::Enum_traits<)") - << op_enum_name << indent(true, R"(>::npos) -`os << json::ast::Number_value::unsigned_integer_to_string(static_cast(value)); -else -`os << get_enumerant_name(value); -)"); - break; - } - break; - } - } - push_indent.finish(); - state << indent("}\n"); + if(ch >= 'a' && ch <= 'z') + ch = ch - 'a' + 'A'; // to uppercase + os << ch; } - state << indent(R"( -void Parse_dump::)") - << dump_function_name << "(const util::optional<" - << get_operand_with_parameters_name(state, operand_kind) - << indent(true, R"(> &value) -{ -`if(value) -)") << indent(2) << dump_function_name - << indent(true, R"((*value); -`else -``os << "nullopt"; -} - -void Parse_dump::)") - << dump_function_name << "(const std::vector<" - << get_operand_with_parameters_name(state, operand_kind) - << indent(true, R"(> &values) -{ -`auto separator = ""; -`os << "{"; -`for(auto &value : values) -`{ -``os << separator; -``separator = ", "; -)") << indent(2) << dump_function_name - << indent(true, R"((value); -`} -`os << "}"; -} -)"); + os << '_'; } - for(auto &instruction : top_level.instructions.instructions) + } + struct Guard_macro + { + friend Generated_output_stream &operator<<(Generated_output_stream &os, Guard_macro) { - auto struct_name = get_enumerant_name(op_enum_name, instruction.opname, true); - state << indent( - "\n" - "void Parse_dump::handle_instruction(") - << struct_name << indent(true, R"( instruction) -{ -`os << ")") << instruction.opname - << indent(true, R"(\n"; -)"); - for(auto &operand : instruction.operands.operands) - { - auto push_indent = state.pushed_indent(); - auto member_name = get_member_name_from_operand(operand); - state << indent("os << \" ") << member_name << indent(true, R"(:"; -)") << indent << Parser_header_generator::get_dump_operand_function_name(operand.kind) - << indent(true, R"((instruction.)") << member_name << indent(true, R"(); -os << "\n"; -)"); - } - state << indent("}\n"); + write_guard_macro(os); + return os; } - write_namespaces_end(state, spirv_namespace_names); + }; + static constexpr Guard_macro guard_macro{}; + 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; + State(const util::filesystem::path &output_directory, const ast::Top_level &top_level) + : top_level(top_level), + spirv_h(output_directory / "spirv.h"), + spirv_cpp(output_directory / "spirv.cpp"), + parser_h(output_directory / "parser.h"), + parser_cpp(output_directory / "parser.cpp") + { + } + void run() + { + constexpr auto automatically_generated_file_warning_comment = + R"(/* This file is automatically generated by generate_spirv_parser. DO NOT MODIFY. */ +)"_sv; + spirv_h << automatically_generated_file_warning_comment << top_level.copyright; + 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"( +#define )" << guard_macro + << R"( +)"; + parser_h << R"(#ifndef )" << guard_macro << R"( +#define )" << guard_macro + << R"( +)"; + spirv_cpp << R"(#include ")" << spirv_h.get_file_path().filename().string() << R"(" +)"; + parser_h << R"( +#include ")" << spirv_h.get_file_path().filename().string() + << R"(" +)"; + parser_cpp << R"(#include ")" << parser_h.get_file_path().filename().string() << R"(" +)"; + spirv_h << R"( +#endif /* )" << guard_macro + << R"( */ +)"; + parser_h << R"( +#endif /* )" << guard_macro + << R"( */ +)"; +#warning finish + spirv_h << R"( +#error generator not finished being implemented +)"; + spirv_h.write_to_file(); + spirv_cpp.write_to_file(); + parser_h.write_to_file(); + parser_cpp.write_to_file(); } }; -std::unique_ptr Generators::make_spirv_header_generator() -{ - return std::unique_ptr(new Spirv_header_generator); -} - -std::unique_ptr Generators::make_spirv_source_generator() -{ - return std::unique_ptr(new Spirv_source_generator); -} +constexpr util::string_view + Spirv_and_parser_generator::State::Generated_output_stream::literal_command; -std::unique_ptr Generators::make_parser_header_generator() +void Spirv_and_parser_generator::run(Generator_args &generator_args, + const ast::Top_level &top_level) const { - return std::unique_ptr(new Parser_header_generator); + State(generator_args.output_directory, top_level).run(); } -std::unique_ptr Generators::make_parser_source_generator() +std::unique_ptr Generators::make_spirv_and_parser_generator() { - return std::unique_ptr(new Parser_source_generator); + return std::unique_ptr(new Spirv_and_parser_generator); } std::vector> Generators::make_all_generators() { std::unique_ptr generators_array[] = { - make_spirv_header_generator(), - make_spirv_source_generator(), - make_parser_header_generator(), - make_parser_source_generator(), + make_spirv_and_parser_generator(), }; // use array then move because you can't move out of an std::initializer_list std::vector> retval; diff --git a/src/generate_spirv_parser/generate.h b/src/generate_spirv_parser/generate.h index b873b22..7e9ffdd 100644 --- a/src/generate_spirv_parser/generate.h +++ b/src/generate_spirv_parser/generate.h @@ -48,12 +48,8 @@ struct Generate_error : public std::runtime_error using runtime_error::runtime_error; }; -class Generator +struct Generator { -private: - struct Tester; - -public: struct Generator_args { std::string output_directory; @@ -66,405 +62,19 @@ public: Generator_args(const Generator_args &) = delete; Generator_args &operator=(const Generator_args &) = delete; }; - -protected: - class Push_indent; - struct Generator_state - { - Generator_args &generator_args; - std::size_t indent_level; - std::string full_output_file_name; - std::string guard_macro_name; - std::ofstream os; - const ast::Top_level &top_level; - std::unordered_map operand_kind_map; - std::unordered_map - operand_has_any_parameters_map; - explicit Generator_state(const Generator *generator, - Generator_args &generator_args, - const ast::Top_level &top_level); - void open_output_file(); - template ())> - Generator_state &operator<<(T &&v) - { - os << std::forward(v); - return *this; - } - Generator_state &operator<<(const ast::Capabilities &v) - { - write_capabilities_set(*this, v); - return *this; - } - Generator_state &operator<<(const ast::Extensions &v) - { - write_extensions_set(*this, v); - return *this; - } - Push_indent pushed_indent(std::ptrdiff_t amount = 1) noexcept; - }; - class Push_indent final - { - Push_indent(const Push_indent &) = delete; - Push_indent &operator=(const Push_indent &) = delete; - - private: - Generator_state *state; - std::ptrdiff_t amount; - - public: - explicit Push_indent(Generator_state &state, std::ptrdiff_t amount = 1) noexcept - : state(&state), - amount(amount) - { - state.indent_level += amount; - } - Push_indent(Push_indent &&rt) noexcept : state(rt.state), amount(rt.amount) - { - rt.state = nullptr; - } - void finish() noexcept - { - assert(state); - state->indent_level -= amount; - state = nullptr; - } - ~Push_indent() - { - if(state) - state->indent_level -= amount; - } - }; - // translates initial '`' (backtick) characters to indentations - struct Indent_interpreted_text - { - const char *text; - std::ptrdiff_t indent_offset; - bool start_indented; - constexpr explicit Indent_interpreted_text(const char *text, - std::ptrdiff_t indent_offset, - bool start_indented) noexcept - : text(text), - indent_offset(indent_offset), - start_indented(start_indented) - { - } - friend Generator_state &operator<<(Generator_state &state, Indent_interpreted_text v) - { - write_indent_interpreted_text(state, v.text, v.indent_offset, v.start_indented); - return state; - } - }; - struct Indent_t - { - std::ptrdiff_t offset; - explicit Indent_t() = default; - constexpr Indent_t operator()(std::ptrdiff_t additional_offset) const noexcept - { - return Indent_t{offset + additional_offset}; - } - constexpr Indent_interpreted_text operator()(const char *text) const noexcept - { - return Indent_interpreted_text(text, offset, false); - } - constexpr Indent_interpreted_text operator()(bool start_indented, const char *text) const - noexcept - { - return Indent_interpreted_text(text, offset, start_indented); - } - friend Generator_state &operator<<(Generator_state &state, Indent_t indent) - { - write_indent(state, indent.offset); - return state; - } - }; - static constexpr auto indent = Indent_t{0}; - enum class Integer_literal_base - { - dec = 0, - hex, - oct - }; - struct Unsigned_integer_literal - { - std::uint64_t value; - Integer_literal_base base; - std::size_t minimum_digit_count; - constexpr Unsigned_integer_literal(std::uint64_t value, - Integer_literal_base base, - std::size_t minimum_digit_count = 1) noexcept - : value(value), - base(base), - minimum_digit_count(minimum_digit_count) - { - } - friend Generator_state &operator<<(Generator_state &state, Unsigned_integer_literal v) - { - write_unsigned_integer_literal(state, v.value, v.base, v.minimum_digit_count); - return state; - } - }; - static constexpr Unsigned_integer_literal unsigned_dec_integer_literal( - std::uint64_t value) noexcept - { - return Unsigned_integer_literal(value, Integer_literal_base::dec); - } - static constexpr Unsigned_integer_literal unsigned_hex_integer_literal( - std::uint64_t value, std::size_t minimum_digit_count = 1) noexcept - { - return Unsigned_integer_literal(value, Integer_literal_base::hex, minimum_digit_count); - } - static constexpr Unsigned_integer_literal unsigned_oct_integer_literal( - std::uint64_t value, std::size_t minimum_digit_count = 1) noexcept - { - return Unsigned_integer_literal(value, Integer_literal_base::oct, minimum_digit_count); - } - struct Signed_integer_literal - { - std::int64_t value; - constexpr explicit Signed_integer_literal(std::int64_t value) noexcept : value(value) - { - } - friend Generator_state &operator<<(Generator_state &state, Signed_integer_literal v) - { - write_signed_integer_literal(state, v.value); - return state; - } - }; - static constexpr Signed_integer_literal signed_integer_literal(std::int64_t value) noexcept - { - return Signed_integer_literal(value); - } - -protected: - const char *const output_base_file_name; - -protected: - static std::string get_guard_macro_name_from_file_name(std::string file_name); - static std::string get_enumerant_name(util::string_view enumeration_name, - std::string enumerant_name, - bool input_name_should_have_prefix) - { - return get_enumerant_name(enumeration_name.data(), - enumeration_name.size(), - std::move(enumerant_name), - input_name_should_have_prefix); - } - static std::string get_enumerant_name(const char *enumeration_name, - std::size_t enumeration_name_size, - std::string enumerant_name, - bool input_name_should_have_prefix); - static void write_indent_absolute(Generator_state &state, std::size_t amount); - static void write_indent(Generator_state &state, std::ptrdiff_t offset) - { - write_indent_absolute(state, state.indent_level + offset); - } - static void write_indent_interpreted_text(Generator_state &state, - const char *text, - std::ptrdiff_t offset, - bool start_indented); - static void write_automatically_generated_file_warning(Generator_state &state); - static void write_copyright_comment(Generator_state &state, const ast::Copyright ©right); - static void write_file_comments(Generator_state &state, const ast::Copyright ©right) - { - write_automatically_generated_file_warning(state); - write_copyright_comment(state, copyright); - } - static void write_file_guard_start(Generator_state &state); - static void write_file_guard_end(Generator_state &state); - static void write_namespace_start(Generator_state &state, const char *namespace_name); - static void write_namespace_start(Generator_state &state, const std::string &namespace_name); - -private: - static void write_namespace_end(Generator_state &state); - -protected: - static void write_namespace_end(Generator_state &state, const char *namespace_name) - { - write_namespace_end(state); - } - static void write_namespace_end(Generator_state &state, const std::string &namespace_name) - { - write_namespace_end(state); - } - static void write_namespaces_start(Generator_state &state, - const char *const *namespace_names, - std::size_t namespace_name_count) - { - for(std::size_t i = 0; i < namespace_name_count; i++) - write_namespace_start(state, namespace_names[i]); - } - static void write_namespaces_start(Generator_state &state, - const std::string *namespace_names, - std::size_t namespace_name_count) - { - for(std::size_t i = 0; i < namespace_name_count; i++) - write_namespace_start(state, namespace_names[i]); - } - static void write_namespaces_end(Generator_state &state, - const char *const *namespace_names, - std::size_t namespace_name_count) - { - for(std::size_t i = 0; i < namespace_name_count; i++) - write_namespace_end(state, namespace_names[namespace_name_count - i - 1]); - state << '\n'; - } - static void write_namespaces_end(Generator_state &state, - const std::string *namespace_names, - std::size_t namespace_name_count) - { - for(std::size_t i = 0; i < namespace_name_count; i++) - write_namespace_end(state, namespace_names[namespace_name_count - i - 1]); - state << '\n'; - } - template - static void write_namespaces_start(Generator_state &state, const T(&namespace_names)[N]) - { - write_namespaces_start(state, namespace_names, N); - } - template - static void write_namespaces_end(Generator_state &state, const T(&namespace_names)[N]) - { - write_namespaces_end(state, namespace_names, N); - } - static void write_namespaces_start(Generator_state &state, - std::initializer_list namespace_names) - { - write_namespaces_start(state, namespace_names.begin(), namespace_names.size()); - } - static void write_namespaces_start(Generator_state &state, - std::initializer_list namespace_names) - { - write_namespaces_start(state, namespace_names.begin(), namespace_names.size()); - } - static void write_namespaces_end(Generator_state &state, - std::initializer_list namespace_names) - { - write_namespaces_end(state, namespace_names.begin(), namespace_names.size()); - } - static void write_namespaces_end(Generator_state &state, - std::initializer_list namespace_names) - { - write_namespaces_end(state, namespace_names.begin(), namespace_names.size()); - } - static void write_unsigned_integer_literal(Generator_state &state, - std::uint64_t value, - Integer_literal_base base, - std::size_t minimum_digit_count); - static void write_signed_integer_literal(Generator_state &state, std::int64_t value); - -private: - struct Get_extensions_visitor; - -protected: - static std::unordered_set get_extensions(const ast::Top_level &top_level); - static void write_capabilities_set(Generator_state &state, - const ast::Capabilities &capabilities); - static void write_extensions_set(Generator_state &state, const ast::Extensions &extensions); - static std::string get_name_from_words(const std::string &words); - static std::string get_member_name_from_operand( - const ast::Instructions::Instruction::Operands::Operand &operand); - static std::string get_member_name_from_parameter( - const ast::Operand_kinds::Operand_kind::Enumerants::Enumerant::Parameters::Parameter - ¶meter); - static std::string get_member_name_from_enumerant( - const ast::Operand_kinds::Operand_kind::Enumerants::Enumerant - &enumerant); - static const ast::Operand_kinds::Operand_kind &get_operand_kind_from_string( - Generator_state &state, const std::string &operand_kind_str) - { - auto *retval = state.operand_kind_map[operand_kind_str]; - if(!retval) - throw Generate_error("operand kind not found: " + operand_kind_str); - return *retval; - } - static bool get_operand_has_any_parameters(Generator_state &state, - const ast::Operand_kinds::Operand_kind &operand_kind) - { - return state.operand_has_any_parameters_map[&operand_kind]; - } - static std::string get_enumerant_parameters_struct_name(util::string_view enumeration_name, - std::string enumerant_name, - bool input_name_should_have_prefix) - { - auto retval = "_" + get_enumerant_name( - enumeration_name, enumerant_name, input_name_should_have_prefix) - + "_parameters"; - retval.insert(retval.begin(), enumeration_name.begin(), enumeration_name.end()); - return retval; - } - static std::string get_operand_with_parameters_name( - Generator_state &state, const ast::Operand_kinds::Operand_kind &operand_kind); - static std::string get_operand_with_parameters_name(Generator_state &state, - const std::string &operand_kind_str) - { - return get_operand_with_parameters_name( - state, get_operand_kind_from_string(state, operand_kind_str)); - } - static std::string get_operand_with_parameters_name( - Generator_state &state, const ast::Instructions::Instruction::Operands::Operand &operand) - { - return get_operand_with_parameters_name(state, - get_operand_kind_from_string(state, operand.kind)); - } - static std::string get_enum_name(std::string operand_kind_str) - { - return operand_kind_str; - } - static std::string get_enum_name(const ast::Operand_kinds::Operand_kind &operand_kind) - { - return get_enum_name(operand_kind.kind); - } - static void write_struct_nonstatic_members_and_constructors(Generator_state &state, - const std::string &struct_name, - const std::string *member_types, - const std::string *member_names, - std::size_t member_count); - static std::vector - get_unique_enumerants( - std::vector enumerants); - -protected: - static constexpr const char *vulkan_cpu_namespace_name = "vulkan_cpu"; - static constexpr const char *spirv_namespace_name = "spirv"; - static constexpr const char *spirv_namespace_names[] = { - vulkan_cpu_namespace_name, spirv_namespace_name, - }; - static constexpr const char *capability_enum_name = "Capability"; - static constexpr const char *extension_enum_name = "Extension"; - static constexpr const char *op_enum_name = "Op"; - -public: - explicit Generator(const char *output_base_file_name) noexcept - : output_base_file_name(output_base_file_name) - { - } + virtual ~Generator() = default; virtual void run(Generator_args &generator_args, const ast::Top_level &top_level) const = 0; void run(Generator_args &&generator_args, const ast::Top_level &top_level) const { run(generator_args, top_level); } - -public: - virtual ~Generator() = default; }; -inline Generator::Push_indent Generator::Generator_state::pushed_indent( - std::ptrdiff_t amount) noexcept -{ - return Push_indent(*this, amount); -} - -struct Spirv_header_generator; -struct Spirv_source_generator; -struct Parser_header_generator; -struct Parser_source_generator; +struct Spirv_and_parser_generator; struct Generators { - static std::unique_ptr make_spirv_header_generator(); - static std::unique_ptr make_spirv_source_generator(); - static std::unique_ptr make_parser_header_generator(); - static std::unique_ptr make_parser_source_generator(); + static std::unique_ptr make_spirv_and_parser_generator(); static std::vector> make_all_generators(); }; }