working on refactoring
authorJacob Lifshay <programmerjake@gmail.com>
Fri, 30 Jun 2017 03:22:36 +0000 (20:22 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Fri, 30 Jun 2017 03:22:36 +0000 (20:22 -0700)
src/generate_spirv_parser/generate-old.cpp [new file with mode: 0644]
src/generate_spirv_parser/generate-old.h [new file with mode: 0644]
src/generate_spirv_parser/generate.cpp
src/generate_spirv_parser/generate.h

diff --git a/src/generate_spirv_parser/generate-old.cpp b/src/generate_spirv_parser/generate-old.cpp
new file mode 100644 (file)
index 0000000..ab78874
--- /dev/null
@@ -0,0 +1,2342 @@
+/*
+ * Copyright 2017 Jacob Lifshay
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+#include "generate-old.h"
+#include "json/json.h"
+#include "util/optional.h"
+#include <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 &copyright)
+{
+    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 &parameter)
+{
+    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 &parameter : 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
+            &parameter)
+    {
+        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
+            &parameter)
+    {
+        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 &parameter : 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 &parameters = util::get<)") << enumerant_parameters_struct_name
+                                  << indent(true, R"(>(value.parameters);
+)");
+                            bool first = true;
+                            for(auto &parameter : 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 &parameters = *value.)") << enumerant_member_name
+                                  << indent(true, R"(;
+)");
+                            bool first = true;
+                            for(auto &parameter : 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 &parameter : 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;
+}
+}
+}
+}
diff --git a/src/generate_spirv_parser/generate-old.h b/src/generate_spirv_parser/generate-old.h
new file mode 100644 (file)
index 0000000..1a40f76
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+ * Copyright 2017 Jacob Lifshay
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifndef GENERATE_SPIRV_PARSER_GENERATE_OLD_H_
+#define GENERATE_SPIRV_PARSER_GENERATE_OLD_H_
+
+#include "ast.h"
+#include "util/string_view.h"
+#include <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 &copyright);
+    static void write_file_comments(Generator_state &state, const ast::Copyright &copyright)
+    {
+        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
+            &parameter);
+    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_ */
index 7a07d35526af6db9880bb6af54894287bdebae5c..5ca92b397ab6106b4c93c032cf67c66552c7eb85 100644 (file)
 #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
@@ -35,2300 +38,585 @@ namespace generate_spirv_parser
 {
 namespace generate
 {
-Generator::Generator_state::Generator_state(const Generator *generator,
-                                            Generator_args &generator_args,
-                                            const ast::Top_level &top_level)
-    : generator_args(generator_args),
-      indent_level(0),
-      full_output_file_name(generator_args.output_directory + "/"
-                            + generator->output_base_file_name),
-      guard_macro_name(get_guard_macro_name_from_file_name(full_output_file_name)),
-      os(),
-      top_level(top_level),
-      operand_kind_map(),
-      operand_has_any_parameters_map()
-{
-    os.exceptions(std::ios::badbit | std::ios::failbit);
-    for(auto &operand_kind : top_level.operand_kinds.operand_kinds)
-    {
-        operand_kind_map.emplace(operand_kind.kind, &operand_kind);
-        bool &has_any_parameters = operand_has_any_parameters_map[&operand_kind];
-        has_any_parameters = false;
-        if(util::holds_alternative<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 &copyright)
-{
-    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 &parameter)
-{
-    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 &parameter : 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
-            &parameter)
-    {
-        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
-            &parameter)
-    {
-        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 &parameter : 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 &parameters = util::get<)") << enumerant_parameters_struct_name
-                                  << indent(true, R"(>(value.parameters);
-)");
-                            bool first = true;
-                            for(auto &parameter : 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 &parameters = *value.)") << enumerant_member_name
-                                  << indent(true, R"(;
-)");
-                            bool first = true;
-                            for(auto &parameter : 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 &parameter : 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;
index b873b2253cf9b76186206ac348bd0e9f4b9d799d..7e9ffddeaa6c4a4a103e4f6cfef7cc71fe74eac6 100644 (file)
@@ -48,12 +48,8 @@ struct Generate_error : public std::runtime_error
     using runtime_error::runtime_error;
 };
 
-class Generator
+struct Generator
 {
-private:
-    struct Tester;
-
-public:
     struct Generator_args
     {
         std::string output_directory;
@@ -66,405 +62,19 @@ public:
         Generator_args(const Generator_args &) = delete;
         Generator_args &operator=(const Generator_args &) = delete;
     };
-
-protected:
-    class Push_indent;
-    struct Generator_state
-    {
-        Generator_args &generator_args;
-        std::size_t indent_level;
-        std::string full_output_file_name;
-        std::string guard_macro_name;
-        std::ofstream os;
-        const ast::Top_level &top_level;
-        std::unordered_map<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 &copyright);
-    static void write_file_comments(Generator_state &state, const ast::Copyright &copyright)
-    {
-        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
-            &parameter);
-    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();
 };
 }