working on adding enum parameters to generated parser
authorJacob Lifshay <programmerjake@gmail.com>
Wed, 21 Jun 2017 03:56:45 +0000 (20:56 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Wed, 21 Jun 2017 03:56:45 +0000 (20:56 -0700)
src/demo/demo.cpp
src/generate_spirv_parser/generate.cpp
src/generate_spirv_parser/generate.h
src/spirv/CMakeLists.txt
src/util/enum.h

index 616ef9591c0e686da49767bd777f53991fd4ee86..d94f438d7171326a8edb2e664f89f8295ec389f1 100644 (file)
@@ -206,19 +206,19 @@ int test_main(int argc, char **argv)
     if(file)
     {
         dump_words(*file);
-        std::ostringstream os;
-        spirv::Parse_dump semantics(os);
+        spirv::Parse_dump semantics(std::cout);
         auto parse_error = spirv::Parser<>::parse(file->data(), file->size(), semantics);
         if(parse_error)
         {
-            std::cerr << "error: " << std::hex << std::uppercase << std::showbase
-                      << parse_error->word_index << ": " << parse_error->message << std::endl;
+            std::cerr << std::hex << std::uppercase;
+            std::cerr << "error: ";
+            if(parse_error->instruction_word_index != 0)
+                std::cerr << "in instruction starting at 0x" << parse_error->instruction_word_index
+                          << ": ";
+            std::cerr << "at 0x" << parse_error->word_index << ": " << parse_error->message
+                      << std::endl;
             return 1;
         }
-        else
-        {
-            std::cout << os.str() << std::flush;
-        }
     }
     return 0;
 }
index 4389960b2329fd8e24af678c1b19dec8848fd5db..fd48e25819e6ab42472001d74684196df4d94753 100644 (file)
@@ -22,7 +22,6 @@
  */
 #include "generate.h"
 #include "json/json.h"
-#include "util/string_view.h"
 #include "util/optional.h"
 #include <limits>
 #include <algorithm>
@@ -30,6 +29,8 @@
 #include <iostream>
 #include <unordered_set>
 
+#error finish converting to use get_enum_with_parameters_struct_name
+
 namespace vulkan_cpu
 {
 namespace generate_spirv_parser
@@ -37,15 +38,39 @@ namespace generate_spirv_parser
 namespace generate
 {
 Generator::Generator_state::Generator_state(const Generator *generator,
-                                            Generator_args &generator_args)
+                                            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()
+      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()
@@ -215,8 +240,8 @@ void Generator::write_indent_interpreted_text(Generator_state &state,
 
 void Generator::write_automatically_generated_file_warning(Generator_state &state)
 {
-    state << R"(/* This file is automatically generated by generate_spirv_parser. DO NOT MODIFY. */
-)";
+    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)
@@ -459,102 +484,103 @@ std::string Generator::get_member_name_from_words(const std::string &words)
         if(is_uppercase_letter(ch))
             ch = ch - 'A' + 'a'; // to lowercase
     }
-    for(const char *reserved_word : {
-            "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",
-        })
+    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)
         {
@@ -581,8 +607,8 @@ struct Generator::Tester
     static void test()
     {
         for(auto &input : {
-                "abc  def", "AbcDef", "ABCDef", "'abc, def'",
-            })
+                    "abc  def", "AbcDef", "ABCDef", "'abc, def'",
+                })
         {
             std::cout << "\"" << input << "\" -> " << get_member_name_from_words(input)
                       << std::endl;
@@ -606,6 +632,14 @@ std::string Generator::get_member_name_from_operand(
     return get_member_name_from_words(operand.kind);
 }
 
+std::string Generator::get_enum_with_parameters_struct_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,
@@ -728,7 +762,7 @@ struct Spirv_header_generator final : public Generator
     }
     virtual void run(Generator_args &generator_args, const ast::Top_level &top_level) const override
     {
-        Generator_state state(this, generator_args);
+        Generator_state state(this, generator_args, top_level);
         state.open_output_file();
         write_file_comments(state, top_level.copyright);
         write_file_guard_start(state);
@@ -851,20 +885,16 @@ constexpr Word magic_number = )")
                         state << ",\n";
                     }
                 }
-                state << "};\n";
-                if(!is_bit_enum)
+                state << "};\n"
+                         "\n"
+                         "vulkan_cpu_util_generate_enum_traits("
+                      << operand_kind->kind;
                 {
-                    state << "\n"
-                             "vulkan_cpu_util_generate_enum_traits("
-                          << operand_kind->kind;
-                    {
-                        auto push_indent = state.pushed_indent();
-                        for(auto &enumerant : unique_enumerants)
-                            state << ",\n" << indent << operand_kind->kind
-                                  << "::" << get_enumerant_name(
-                                                 operand_kind->kind, enumerant.enumerant, false);
-                        state << ");\n";
-                    }
+                    auto push_indent = state.pushed_indent();
+                    for(auto &enumerant : unique_enumerants)
+                        state << ",\n" << indent << operand_kind->kind << "::"
+                              << get_enumerant_name(operand_kind->kind, enumerant.enumerant, false);
+                    state << ");\n";
                 }
                 state << "\n"
                          "constexpr const char *get_enumerant_name("
@@ -985,7 +1015,8 @@ constexpr Word magic_number = )")
                 auto &doc = util::get<ast::Operand_kinds::Operand_kind::Doc>(operand_kind->value);
                 auto base_type = "std::vector<Word>";
                 if(operand_kind->kind == "LiteralInteger")
-                    base_type = "std::uint64_t";
+                    base_type = "std::uint32_t"; // TODO: fix after determining if LiteralInteger
+                // can be multiple words
                 else if(operand_kind->kind == "LiteralString")
                     base_type = "std::string";
                 else if(operand_kind->kind == "LiteralExtInstInteger")
@@ -1182,7 +1213,7 @@ struct Spirv_source_generator final : public Generator
     }
     virtual void run(Generator_args &generator_args, const ast::Top_level &top_level) const override
     {
-        Generator_state state(this, generator_args);
+        Generator_state state(this, generator_args, top_level);
         state.open_output_file();
         write_file_comments(state, top_level.copyright);
         state << "#include \"spirv.h\"\n";
@@ -1194,13 +1225,21 @@ struct Parser_header_generator final : public Generator
     Parser_header_generator() : Generator("parser.h")
     {
     }
-    static std::string get_dump_function_name(std::string kind)
+    static std::string get_dump_operand_function_name(std::string kind)
     {
         return "dump_operand_" + std::move(kind);
     }
+    static std::string get_parse_operand_function_name(std::string kind)
+    {
+        return "parse_operand_" + std::move(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);
+        Generator_state state(this, generator_args, top_level);
         state.open_output_file();
         write_file_comments(state, top_level.copyright);
         write_file_guard_start(state);
@@ -1208,6 +1247,7 @@ struct Parser_header_generator final : public Generator
 #include <memory>
 #include <ostream>
 #include "util/optional.h"
+#include "json/json.h"
 #include <vector>
 
 )";
@@ -1215,9 +1255,11 @@ struct Parser_header_generator final : public Generator
         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::string message) noexcept
+`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))
 `{
 `}
@@ -1230,7 +1272,10 @@ struct Parser_header_generator final : public Generator
         {
             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::string message) = 0;
+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)
             {
@@ -1252,7 +1297,10 @@ struct Parse_dump final : public Parse_semantics_generic
         {
             auto push_indent = state.pushed_indent();
             state << indent(
-                R"(virtual std::unique_ptr<Parse_error> handle_error(std::size_t word_index, std::string message) override;
+                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)
             {
@@ -1262,7 +1310,7 @@ struct Parse_dump final : public Parse_semantics_generic
             }
             for(auto &operand_kind : top_level.operand_kinds.operand_kinds)
             {
-                auto dump_function_name = get_dump_function_name(operand_kind.kind);
+                auto dump_function_name = get_dump_operand_function_name(operand_kind.kind);
                 state << indent("void ") << dump_function_name << "(const " << operand_kind.kind
                       << " &v);\n";
                 state << indent("void ") << dump_function_name << "(const util::optional<"
@@ -1279,21 +1327,284 @@ struct Parse_dump final : public Parse_semantics_generic
                  "{\n";
         {
             auto push_indent = state.pushed_indent();
-            state << indent(
-                R"(static std::unique_ptr<Parse_error> parse(const Word *words, std::size_t word_count, Semantics &semantics)
+            for(auto &operand_kind : top_level.operand_kinds.operand_kinds)
+            {
+                auto parse_function_name = get_parse_operand_function_name(operand_kind.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, )")
+                    << operand_kind.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:
+                {
+                    state << indent(R"(value = static_cast<)") << operand_kind.kind
+                          << indent(true, R"(>(words[word_index++]);
+)");
+                    break;
+                }
+                case ast::Operand_kinds::Operand_kind::Category::value_enum:
+                {
+                    state << indent(R"(value = static_cast<)") << operand_kind.kind
+                          << indent(true, R"(>(words[word_index]);
+if(util::Enum_traits<)") << operand_kind.kind
+                          << indent(true, R"(>::find_value(value) == util::Enum_traits<)")
+                          << operand_kind.kind << 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:
+                {
+                    if(operand_kind.kind == "LiteralInteger")
+                    {
+                        // TODO: fix after determining if LiteralInteger can be multiple words
+                        state << indent(R"(value = words[word_index++];
+)");
+                    }
+                    else if(operand_kind.kind == "LiteralExtInstInteger")
+                    {
+                        state << indent(R"(value = words[word_index++];
+)");
+                    }
+                    else if(operand_kind.kind == "LiteralString")
+                    {
+                        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;
+``}
+`}
+}
 )");
+                    }
+                    else if(operand_kind.kind == "LiteralSpecConstantOpInteger")
+                    {
+#warning finish
+                        state << indent(R"(value = static_cast<)") << op_enum_name
+                              << indent(true, R"(>(words[word_index++]);
+)");
+                    }
+                    else
+                    {
+                        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;
+                }
+                }
+                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(R"(std::size_t word_index = 0;
-if(word_index >= word_count)
-`return semantics.handle_error(word_index, "hit EOF when parsing magic number");
-if(words[word_index] != magic_number)
-`return semantics.handle_error(word_index, "invalid magic number");
-return nullptr;
+                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.kind);
+                    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("}\n");
+            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;
+}
+)");
 #warning finish
         }
         state << "};\n";
@@ -1310,10 +1621,11 @@ struct Parser_source_generator final : public Generator
     }
     virtual void run(Generator_args &generator_args, const ast::Top_level &top_level) const override
     {
-        Generator_state state(this, generator_args);
+        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
@@ -1325,15 +1637,30 @@ struct Parser_source_generator final : public Generator
 }
 }
 
-std::unique_ptr<Parse_error> Parse_dump::handle_error(std::size_t word_index, std::string message)
+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, std::move(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_function_name(operand_kind.kind);
+                Parser_header_generator::get_dump_operand_function_name(operand_kind.kind);
             {
                 state << indent(R"(
 void Parse_dump::)") << dump_function_name
@@ -1345,29 +1672,130 @@ void Parse_dump::)") << dump_function_name
                 {
                 case ast::Operand_kinds::Operand_kind::Category::bit_enum:
                 {
-                    auto &enumerants =
-                        util::get<ast::Operand_kinds::Operand_kind::Enumerants>(operand_kind.value);
-#warning finish
+                    state << indent(R"(Word bits = static_cast<Word>(v);
+util::Enum_set<)") << operand_kind.kind
+                          << indent(true, R"(> enum_set{};
+for(auto value : util::Enum_traits<)")
+                          << operand_kind.kind << indent(true, R"(>::values)
+{
+`if(static_cast<Word>(value) == 0)
+`{
+``if(v == value)
+```enum_set.insert(value);
+``continue;
+`}
+`if((bits & static_cast<Word>(value)) == static_cast<Word>(value))
+`{
+``bits &= ~static_cast<Word>(value);
+``enum_set.insert(value);
+`}
+}
+bool first = true;
+for(auto value : enum_set)
+{
+`if(first)
+``first = false;
+`else
+``os << " | ";
+`os << get_enumerant_name(value);
+}
+if(bits)
+{
+`if(!first)
+``os << " | ";
+`os << json::ast::Number_value::append_unsigned_integer_to_string(bits, "0x", 0x10);
+}
+else if(first)
+{
+`os << "0";
+}
+)");
                     break;
                 }
                 case ast::Operand_kinds::Operand_kind::Category::value_enum:
                 {
-#warning finish
+                    state << indent(R"(if(util::Enum_traits<)") << operand_kind.kind
+                          << indent(true, R"(>::find_value(v) == util::Enum_traits<)")
+                          << operand_kind.kind << indent(true, R"(>::npos)
+`os << json::ast::Number_value::unsigned_integer_to_string(static_cast<Word>(v));
+else
+`os << get_enumerant_name(v);
+)");
                     break;
                 }
                 case ast::Operand_kinds::Operand_kind::Category::composite:
                 {
-#warning finish
+                    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])
+                              << "(v."
+                              << 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:
                 {
-#warning finish
+                    state << indent(
+                        R"(os << json::ast::Number_value::append_unsigned_integer_to_string(v, "#");
+)");
                     break;
                 }
                 case ast::Operand_kinds::Operand_kind::Category::literal:
                 {
-#warning finish
+                    if(operand_kind.kind == "LiteralInteger")
+                    {
+                        state << indent(
+                            R"(os << json::ast::Number_value::append_unsigned_integer_to_string(v, "0x");
+)");
+                    }
+                    else if(operand_kind.kind == "LiteralExtInstInteger")
+                    {
+                        state << indent(
+                            R"(os << json::ast::Number_value::append_unsigned_integer_to_string(v, "0x");
+)");
+                    }
+                    else if(operand_kind.kind == "LiteralString")
+                    {
+                        state << indent(
+                            R"(json::ast::String_value::write(os, v);
+)");
+                    }
+                    else if(operand_kind.kind == "LiteralSpecConstantOpInteger")
+                    {
+                        state << indent(R"(if(util::Enum_traits<)") << op_enum_name
+                              << indent(true, R"(>::find_value(v) == util::Enum_traits<)")
+                              << op_enum_name << indent(true, R"(>::npos)
+`os << json::ast::Number_value::unsigned_integer_to_string(static_cast<Word>(v));
+else
+`os << get_enumerant_name(v);
+)");
+                    }
+                    else
+                    {
+                        state << indent(
+                            R"(static_assert(std::is_same<decltype(v), const std::vector<Word> &>::value, "missing dump code for operand kind");
+auto separator = "";
+os << "{";
+for(Word value : v)
+{
+`os << separator;
+`separator = ", ";
+`os << json::ast::Number_value::append_unsigned_integer_to_string(value, "0x", 0x10, 8);
+}
+os << "}";
+)");
+                    }
                     break;
                 }
                 }
@@ -1402,7 +1830,6 @@ void Parse_dump::)")
 `os << "}";
 }
 )");
-#warning finish
         }
         for(auto &instruction : top_level.instructions.instructions)
         {
@@ -1420,7 +1847,7 @@ void Parse_dump::)")
                 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_function_name(operand.kind)
+)") << indent << Parser_header_generator::get_dump_operand_function_name(operand.kind)
                       << indent(true, R"((instruction.)") << member_name << indent(true, R"();
 os << "\n";
 )");
index 04aadfcac92231765b24017880667d1aad9e89c4..7a76acb9e9c75d8b4da9dcddd72f52c184537691 100644 (file)
@@ -25,6 +25,7 @@
 #define GENERATE_SPIRV_PARSER_GENERATE_H_
 
 #include "ast.h"
+#include "util/string_view.h"
 #include <fstream>
 #include <memory>
 #include <string>
@@ -32,7 +33,9 @@
 #include <type_traits>
 #include <cstdint>
 #include <unordered_set>
+#include <unordered_map>
 #include <vector>
+#include <stdexcept>
 
 namespace vulkan_cpu
 {
@@ -40,6 +43,11 @@ namespace generate_spirv_parser
 {
 namespace generate
 {
+struct Generate_error : public std::runtime_error
+{
+    using runtime_error::runtime_error;
+};
+
 class Generator
 {
 private:
@@ -68,7 +76,13 @@ protected:
         std::string full_output_file_name;
         std::string guard_macro_name;
         std::ofstream os;
-        explicit Generator_state(const Generator *generator, Generator_args &generator_args);
+        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)
@@ -226,7 +240,7 @@ protected:
 
 protected:
     static std::string get_guard_macro_name_from_file_name(std::string file_name);
-    static std::string get_enumerant_name(const std::string &enumeration_name,
+    static std::string get_enumerant_name(util::string_view enumeration_name,
                                           std::string enumerant_name,
                                           bool input_name_should_have_prefix)
     {
@@ -235,15 +249,6 @@ protected:
                                   std::move(enumerant_name),
                                   input_name_should_have_prefix);
     }
-    static std::string get_enumerant_name(const char *enumeration_name,
-                                          std::string enumerant_name,
-                                          bool input_name_should_have_prefix)
-    {
-        return get_enumerant_name(enumeration_name,
-                                  std::char_traits<char>::length(enumeration_name),
-                                  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,
@@ -358,6 +363,33 @@ protected:
     static std::string get_member_name_from_words(const std::string &words);
     static std::string get_member_name_from_operand(
         const ast::Instructions::Instruction::Operands::Operand &operand);
+    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_enum_with_parameters_struct_name(
+        Generator_state &state, const ast::Operand_kinds::Operand_kind &operand_kind);
+    static std::string get_enum_with_parameters_struct_name(Generator_state &state,
+                                                            const std::string &operand_kind_str)
+    {
+        return get_enum_with_parameters_struct_name(
+            state, get_operand_kind_from_string(state, operand_kind_str));
+    }
     static void write_struct_nonstatic_members_and_constructors(Generator_state &state,
                                                                 const std::string &struct_name,
                                                                 const std::string *member_types,
index 31995bcc85eafb90c889bdf79ad3bf2389d127d7..f85af006040ea6aa402ef12348a0cf4362011643 100644 (file)
@@ -38,5 +38,5 @@ add_custom_command(OUTPUT ${spirv_parser_sources} ${spirv_parser_headers}
                    COMMENT "Generating SPIR-V Parser")
 set(sources ${sources} ${spirv_parser_sources})
 add_library(spirv STATIC ${sources})
-target_link_libraries(spirv util)
+target_link_libraries(spirv util json)
 target_include_directories(spirv PUBLIC ${spirv_parser_generated_include_dir})
index 51e082600c999e103c9c9abac80178a2d8ed7c36..11e7bc2c21d4e070af5c139fa8ba3c64937acbb7 100644 (file)
@@ -140,7 +140,7 @@ public:
         }
         else if(value_count < binary_search_transition)
         {
-            retval = -1;
+            retval = npos;
             for(std::size_t i = 0; i < value_count; i++)
             {
                 if(values[i] == value)