--- /dev/null
+/*
+ * 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 <limits>
+#include <algorithm>
+#include <cstdlib>
+#include <iostream>
+#include <unordered_set>
+
+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<ast::Operand_kinds::Operand_kind::Enumerants>(
+ operand_kind.value))
+ {
+ auto &enumerants =
+ util::get<ast::Operand_kinds::Operand_kind::Enumerants>(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<std::uint16_t>::max();
+ constexpr std::uint64_t max_unsigned_long_value = std::numeric_limits<std::uint32_t>::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<std::int16_t>::max();
+ constexpr std::int64_t min_int_value = std::numeric_limits<std::int16_t>::min();
+ constexpr std::int64_t max_long_value = std::numeric_limits<std::int32_t>::max();
+ constexpr std::int64_t min_long_value = std::numeric_limits<std::int32_t>::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<std::string> &retval;
+ constexpr Get_extensions_visitor(std::unordered_set<std::string> &retval) noexcept
+ : retval(retval)
+ {
+ }
+ template <typename T>
+ void operator()(const T &)
+ {
+ }
+ void operator()(const ast::Extensions &extensions)
+ {
+ for(auto &extension : extensions.extensions)
+ retval.insert(extension);
+ }
+};
+
+std::unordered_set<std::string> Generator::get_extensions(const ast::Top_level &top_level)
+{
+ std::unordered_set<std::string> 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<std::size_t> 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<ast::Operand_kinds::Operand_kind::Enumerants::Enumerant>
+ Generator::get_unique_enumerants(
+ std::vector<ast::Operand_kinds::Operand_kind::Enumerants::Enumerant> enumerants)
+{
+ std::unordered_set<std::uint32_t> 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 <cstdint>
+#include "util/enum.h"
+#include "util/optional.h"
+#include "util/variant.h"
+#include <vector>
+
+)");
+ 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<std::string> 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<const ast::Operand_kinds::Operand_kind *> 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<ast::Operand_kinds::Operand_kind::Enumerants>(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<ast::Operand_kinds::Operand_kind::Bases>(operand_kind->value);
+ state << "\n"
+ "struct "
+ << operand_kind->kind << "\n"
+ "{\n";
+ auto push_indent = state.pushed_indent();
+ std::vector<std::string> member_types;
+ std::vector<std::string> 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<ast::Operand_kinds::Operand_kind::Doc>(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<ast::Operand_kinds::Operand_kind::Doc>(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<Word>";
+ 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<ast::Operand_kinds::Operand_kind::Enumerants>(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<std::string> member_types;
+ std::vector<std::string> 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<std::string> member_types;
+ std::vector<std::string> 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::variant<util::monostate)");
+ for(auto &enumerant : unique_enumerants)
+ {
+ if(enumerant.parameters.empty())
+ continue;
+ state << ",\n" << indent(2)
+ << get_enumerant_parameters_struct_name(
+ operand_kind->kind, 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<const ast::Instructions::Instruction *> 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<std::string> member_names;
+ std::vector<std::string> 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 <memory>
+#include <ostream>
+#include "util/optional.h"
+#include "json/json.h"
+#include <vector>
+
+)";
+ 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<Parse_error> 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<Parse_error> 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 <typename Semantics = Parse_semantics_generic>\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_error> )") << 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<ast::Operand_kinds::Operand_kind::Enumerants>(
+ 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<Word>(value.value) & static_cast<Word>()")
+ << enum_name << "::" << enumerant_name
+ << indent(true, R"()) == static_cast<Word>()") << 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<ast::Operand_kinds::Operand_kind::Enumerants>(
+ 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<ast::Operand_kinds::Operand_kind::Bases>(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<Id>(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<decltype(value), std::vector<Word> &>::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_error> )") << 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> 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_error> parse_instruction(const Word *words, std::size_t word_count, Semantics &semantics, std::size_t error_instruction_index)
+{
+`Op op = static_cast<Op>(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<Word>(op), "unknown instruction: 0x", 0x10));
+}
+static std::unique_ptr<Parse_error> 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 <type_traits>\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_error> Parse_dump::handle_error(std::size_t word_index, std::size_t instruction_word_index, std::string message)
+{
+`return std::unique_ptr<Parse_error>(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<Word>(value)")
+ << (operand_has_any_parameters ? ".value" : "") << indent(true, R"();
+bool first = true;
+)");
+ auto enum_name = get_enum_name(operand_kind);
+ auto &enumerants =
+ util::get<ast::Operand_kinds::Operand_kind::Enumerants>(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<Word>()") << scoped_enumerant_name
+ << indent(true, R"()) == static_cast<Word>()")
+ << scoped_enumerant_name << indent(true, R"())
+{
+`bits &= ~static_cast<Word>()")
+ << 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<ast::Operand_kinds::Operand_kind::Enumerants>(
+ 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<Word>(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<Word>(value));
+else
+`os << get_enumerant_name(value);
+)");
+ }
+ break;
+ }
+ case ast::Operand_kinds::Operand_kind::Category::composite:
+ {
+ auto &bases =
+ util::get<ast::Operand_kinds::Operand_kind::Bases>(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<decltype(value), const std::vector<Word> &>::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<Word>(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<Generator> Generators::make_spirv_header_generator()
+{
+ return std::unique_ptr<Generator>(new Spirv_header_generator);
+}
+
+std::unique_ptr<Generator> Generators::make_spirv_source_generator()
+{
+ return std::unique_ptr<Generator>(new Spirv_source_generator);
+}
+
+std::unique_ptr<Generator> Generators::make_parser_header_generator()
+{
+ return std::unique_ptr<Generator>(new Parser_header_generator);
+}
+
+std::unique_ptr<Generator> Generators::make_parser_source_generator()
+{
+ return std::unique_ptr<Generator>(new Parser_source_generator);
+}
+
+std::vector<std::unique_ptr<Generator>> Generators::make_all_generators()
+{
+ std::unique_ptr<Generator> 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<std::unique_ptr<Generator>> retval;
+ retval.reserve(sizeof(generators_array) / sizeof(generators_array[0]));
+ for(auto &generator : generators_array)
+ retval.push_back(std::move(generator));
+ return retval;
+}
+}
+}
+}
--- /dev/null
+/*
+ * 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 <fstream>
+#include <memory>
+#include <string>
+#include <cassert>
+#include <type_traits>
+#include <cstdint>
+#include <unordered_set>
+#include <unordered_map>
+#include <vector>
+#include <stdexcept>
+
+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<std::string, const ast::Operand_kinds::Operand_kind *> operand_kind_map;
+ std::unordered_map<const ast::Operand_kinds::Operand_kind *, bool>
+ 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 <typename T, typename = decltype(os << std::declval<T>())>
+ Generator_state &operator<<(T &&v)
+ {
+ os << std::forward<T>(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 <typename T, std::size_t N>
+ static void write_namespaces_start(Generator_state &state, const T(&namespace_names)[N])
+ {
+ write_namespaces_start(state, namespace_names, N);
+ }
+ template <typename T, std::size_t N>
+ 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<std::string> namespace_names)
+ {
+ write_namespaces_start(state, namespace_names.begin(), namespace_names.size());
+ }
+ static void write_namespaces_start(Generator_state &state,
+ std::initializer_list<const char *> namespace_names)
+ {
+ write_namespaces_start(state, namespace_names.begin(), namespace_names.size());
+ }
+ static void write_namespaces_end(Generator_state &state,
+ std::initializer_list<std::string> namespace_names)
+ {
+ write_namespaces_end(state, namespace_names.begin(), namespace_names.size());
+ }
+ static void write_namespaces_end(Generator_state &state,
+ std::initializer_list<const char *> 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<std::string> 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<ast::Operand_kinds::Operand_kind::Enumerants::Enumerant>
+ get_unique_enumerants(
+ std::vector<ast::Operand_kinds::Operand_kind::Enumerants::Enumerant> 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<Generator> make_spirv_header_generator();
+ static std::unique_ptr<Generator> make_spirv_source_generator();
+ static std::unique_ptr<Generator> make_parser_header_generator();
+ static std::unique_ptr<Generator> make_parser_source_generator();
+ static std::vector<std::unique_ptr<Generator>> make_all_generators();
+};
+}
+}
+}
+
+#endif /* GENERATE_SPIRV_PARSER_GENERATE_OLD_H_ */
#include "generate.h"
#include "json/json.h"
#include "util/optional.h"
+#include "util/filesystem.h"
#include <limits>
#include <algorithm>
#include <cstdlib>
#include <iostream>
+#include <sstream>
+#include <deque>
#include <unordered_set>
namespace vulkan_cpu
{
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<ast::Operand_kinds::Operand_kind::Enumerants>(
- operand_kind.value))
- {
- auto &enumerants =
- util::get<ast::Operand_kinds::Operand_kind::Enumerants>(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<char> 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 <typename Fn>
+ 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<std::uint16_t>::max();
- constexpr std::uint64_t max_unsigned_long_value = std::numeric_limits<std::uint32_t>::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<std::int16_t>::max();
- constexpr std::int64_t min_int_value = std::numeric_limits<std::int16_t>::min();
- constexpr std::int64_t max_long_value = std::numeric_limits<std::int32_t>::max();
- constexpr std::int64_t min_long_value = std::numeric_limits<std::int32_t>::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<std::string> &retval;
- constexpr Get_extensions_visitor(std::unordered_set<std::string> &retval) noexcept
- : retval(retval)
- {
- }
- template <typename T>
- void operator()(const T &)
- {
- }
- void operator()(const ast::Extensions &extensions)
- {
- for(auto &extension : extensions.extensions)
- retval.insert(extension);
- }
-};
-
-std::unordered_set<std::string> Generator::get_extensions(const ast::Top_level &top_level)
-{
- std::unordered_set<std::string> 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<std::size_t> 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<decltype(value)>::radix == 2, "");
+ constexpr std::size_t buffer_size = std::numeric_limits<decltype(value)>::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<decltype(value)>::radix == 2, "");
+ constexpr std::size_t buffer_size =
+ std::numeric_limits<decltype(value)>::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 <typename T>
+ 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<ast::Operand_kinds::Operand_kind::Enumerants::Enumerant>
- Generator::get_unique_enumerants(
- std::vector<ast::Operand_kinds::Operand_kind::Enumerants::Enumerant> enumerants)
-{
- std::unordered_set<std::uint32_t> 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 <cstdint>
-#include "util/enum.h"
-#include "util/optional.h"
-#include "util/variant.h"
-#include <vector>
+ 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<std::string> 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<const ast::Operand_kinds::Operand_kind *> 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<std::size_t> 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<ast::Operand_kinds::Operand_kind::Enumerants>(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<ast::Operand_kinds::Operand_kind::Bases>(operand_kind->value);
- state << "\n"
- "struct "
- << operand_kind->kind << "\n"
- "{\n";
- auto push_indent = state.pushed_indent();
- std::vector<std::string> member_types;
- std::vector<std::string> 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<ast::Operand_kinds::Operand_kind::Doc>(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<ast::Operand_kinds::Operand_kind::Doc>(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<Word>";
- 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<ast::Operand_kinds::Operand_kind::Enumerants>(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<std::string> member_types;
- std::vector<std::string> 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<std::string> member_types;
- std::vector<std::string> 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::variant<util::monostate)");
- for(auto &enumerant : unique_enumerants)
- {
- if(enumerant.parameters.empty())
- continue;
- state << ",\n" << indent(2)
- << get_enumerant_parameters_struct_name(
- operand_kind->kind, 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<const ast::Instructions::Instruction *> 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<std::string> member_names;
- std::vector<std::string> 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 <memory>
-#include <ostream>
-#include "util/optional.h"
-#include "json/json.h"
-#include <vector>
-
-)";
- 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<Parse_error> 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<Parse_error> 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 <typename Semantics = Parse_semantics_generic>\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_error> )") << 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<ast::Operand_kinds::Operand_kind::Enumerants>(
- 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<Word>(value.value) & static_cast<Word>()")
- << enum_name << "::" << enumerant_name
- << indent(true, R"()) == static_cast<Word>()") << 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<ast::Operand_kinds::Operand_kind::Enumerants>(
- 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<ast::Operand_kinds::Operand_kind::Bases>(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<Id>(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<decltype(value), std::vector<Word> &>::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_error> )") << 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> 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_error> parse_instruction(const Word *words, std::size_t word_count, Semantics &semantics, std::size_t error_instruction_index)
-{
-`Op op = static_cast<Op>(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<Word>(op), "unknown instruction: 0x", 0x10));
-}
-static std::unique_ptr<Parse_error> 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 <type_traits>\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_error> Parse_dump::handle_error(std::size_t word_index, std::size_t instruction_word_index, std::string message)
-{
-`return std::unique_ptr<Parse_error>(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<Word>(value)")
- << (operand_has_any_parameters ? ".value" : "") << indent(true, R"();
-bool first = true;
-)");
- auto enum_name = get_enum_name(operand_kind);
- auto &enumerants =
- util::get<ast::Operand_kinds::Operand_kind::Enumerants>(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<Word>()") << scoped_enumerant_name
- << indent(true, R"()) == static_cast<Word>()")
- << scoped_enumerant_name << indent(true, R"())
-{
-`bits &= ~static_cast<Word>()")
- << 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<ast::Operand_kinds::Operand_kind::Enumerants>(
- 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<Word>(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<Word>(value));
-else
-`os << get_enumerant_name(value);
-)");
- }
- break;
- }
- case ast::Operand_kinds::Operand_kind::Category::composite:
- {
- auto &bases =
- util::get<ast::Operand_kinds::Operand_kind::Bases>(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<decltype(value), const std::vector<Word> &>::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<Word>(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<Generator> Generators::make_spirv_header_generator()
-{
- return std::unique_ptr<Generator>(new Spirv_header_generator);
-}
-
-std::unique_ptr<Generator> Generators::make_spirv_source_generator()
-{
- return std::unique_ptr<Generator>(new Spirv_source_generator);
-}
+constexpr util::string_view
+ Spirv_and_parser_generator::State::Generated_output_stream::literal_command;
-std::unique_ptr<Generator> 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<Generator>(new Parser_header_generator);
+ State(generator_args.output_directory, top_level).run();
}
-std::unique_ptr<Generator> Generators::make_parser_source_generator()
+std::unique_ptr<Generator> Generators::make_spirv_and_parser_generator()
{
- return std::unique_ptr<Generator>(new Parser_source_generator);
+ return std::unique_ptr<Generator>(new Spirv_and_parser_generator);
}
std::vector<std::unique_ptr<Generator>> Generators::make_all_generators()
{
std::unique_ptr<Generator> 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<std::unique_ptr<Generator>> retval;
using runtime_error::runtime_error;
};
-class Generator
+struct Generator
{
-private:
- struct Tester;
-
-public:
struct Generator_args
{
std::string output_directory;
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<std::string, const ast::Operand_kinds::Operand_kind *> operand_kind_map;
- std::unordered_map<const ast::Operand_kinds::Operand_kind *, bool>
- 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 <typename T, typename = decltype(os << std::declval<T>())>
- Generator_state &operator<<(T &&v)
- {
- os << std::forward<T>(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 <typename T, std::size_t N>
- static void write_namespaces_start(Generator_state &state, const T(&namespace_names)[N])
- {
- write_namespaces_start(state, namespace_names, N);
- }
- template <typename T, std::size_t N>
- 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<std::string> namespace_names)
- {
- write_namespaces_start(state, namespace_names.begin(), namespace_names.size());
- }
- static void write_namespaces_start(Generator_state &state,
- std::initializer_list<const char *> namespace_names)
- {
- write_namespaces_start(state, namespace_names.begin(), namespace_names.size());
- }
- static void write_namespaces_end(Generator_state &state,
- std::initializer_list<std::string> namespace_names)
- {
- write_namespaces_end(state, namespace_names.begin(), namespace_names.size());
- }
- static void write_namespaces_end(Generator_state &state,
- std::initializer_list<const char *> 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<std::string> 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<ast::Operand_kinds::Operand_kind::Enumerants::Enumerant>
- get_unique_enumerants(
- std::vector<ast::Operand_kinds::Operand_kind::Enumerants::Enumerant> 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<Generator> make_spirv_header_generator();
- static std::unique_ptr<Generator> make_spirv_source_generator();
- static std::unique_ptr<Generator> make_parser_header_generator();
- static std::unique_ptr<Generator> make_parser_source_generator();
+ static std::unique_ptr<Generator> make_spirv_and_parser_generator();
static std::vector<std::unique_ptr<Generator>> make_all_generators();
};
}