add code to parse extinst json files; still need to add to generate.cpp
authorJacob Lifshay <programmerjake@gmail.com>
Thu, 29 Jun 2017 07:29:08 +0000 (00:29 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Thu, 29 Jun 2017 07:29:08 +0000 (00:29 -0700)
src/generate_spirv_parser/ast.cpp
src/generate_spirv_parser/ast.h
src/generate_spirv_parser/generate_spirv_parser.cpp
src/generate_spirv_parser/parser.cpp
src/generate_spirv_parser/parser.h
src/spirv/CMakeLists.txt

index 12dc6f104b9934a7af1251168e67737decd032a9..5619228b8987fb9ec55e21da1efd8db4bfc39dec 100644 (file)
@@ -28,6 +28,11 @@ namespace generate_spirv_parser
 {
 namespace ast
 {
+constexpr const char *Extension_instruction_set::json_file_name_prefix;
+constexpr const char *Extension_instruction_set::json_file_name_suffix;
+
+constexpr const char *Top_level::core_grammar_json_file_name;
+
 namespace
 {
 std::string to_hex_string(std::uint32_t v, std::size_t min_digit_count)
@@ -201,18 +206,53 @@ json::ast::Value Operand_kinds::to_json() const
     return json::ast::Value(make_empty_location(), std::move(retval));
 }
 
-json::ast::Value Top_level::to_json() const
+util::optional<std::string> Extension_instruction_set::get_import_name_from_instruction_set_name(
+    util::string_view instruction_set_name)
 {
-    json::ast::Object retval;
-    retval.values["copyright"] = copyright.to_json();
-    retval.values["magic_number"] =
+    if(instruction_set_name == "glsl.std.450")
+        return "GLSL.std.450";
+    if(instruction_set_name == "opencl.std.100")
+        return "OpenCL.std";
+    return {};
+}
+
+Json_file Extension_instruction_set::to_json() const
+{
+    json::ast::Object grammar;
+    grammar.values["copyright"] = copyright.to_json();
+    grammar.values["version"] = json::ast::Value(make_empty_location(), version);
+    grammar.values["revision"] = json::ast::Value(make_empty_location(), revision);
+    grammar.values["instructions"] = instructions.to_json();
+    auto file_name = json_file_name_prefix + instruction_set_name + json_file_name_suffix;
+    for(char &ch : file_name)
+    {
+        if(ch >= 'A' && ch <= 'Z')
+            ch = ch - 'A' + 'a'; // to lower
+    }
+    return Json_file(std::move(file_name),
+                     json::ast::Value(make_empty_location(), std::move(grammar)),
+                     import_name);
+}
+
+std::vector<Json_file> Top_level::to_json() const
+{
+    json::ast::Object core_grammar;
+    core_grammar.values["copyright"] = copyright.to_json();
+    core_grammar.values["magic_number"] =
         json::ast::Value(make_empty_location(), to_hex_string(magic_number, 8));
-    retval.values["major_version"] = json::ast::Value(make_empty_location(), major_version);
-    retval.values["minor_version"] = json::ast::Value(make_empty_location(), minor_version);
-    retval.values["revision"] = json::ast::Value(make_empty_location(), revision);
-    retval.values["instructions"] = instructions.to_json();
-    retval.values["operand_kinds"] = operand_kinds.to_json();
-    return json::ast::Value(make_empty_location(), std::move(retval));
+    core_grammar.values["major_version"] = json::ast::Value(make_empty_location(), major_version);
+    core_grammar.values["minor_version"] = json::ast::Value(make_empty_location(), minor_version);
+    core_grammar.values["revision"] = json::ast::Value(make_empty_location(), revision);
+    core_grammar.values["instructions"] = instructions.to_json();
+    core_grammar.values["operand_kinds"] = operand_kinds.to_json();
+    std::vector<Json_file> retval;
+    retval.reserve(extension_instruction_sets.size() + 1);
+    retval.push_back(Json_file("spirv.core.grammar.json",
+                               json::ast::Value(make_empty_location(), std::move(core_grammar)),
+                               util::nullopt));
+    for(auto &extension_instruction_set : extension_instruction_sets)
+        retval.push_back(extension_instruction_set.to_json());
+    return retval;
 }
 }
 }
index e664051264ac8d231e1379f25605060bfd00ed63..fe4ece82484bcd6920d83529d2a3ce03752f80d1 100644 (file)
@@ -475,8 +475,60 @@ struct Operand_kinds
     }
 };
 
+struct Json_file
+{
+    std::string file_name;
+    json::ast::Value json;
+    util::optional<std::string> extension_instruction_set_import_name;
+    Json_file(std::string file_name,
+              json::ast::Value json,
+              util::optional<std::string> extension_instruction_set_import_name)
+        : file_name(std::move(file_name)),
+          json(std::move(json)),
+          extension_instruction_set_import_name(std::move(extension_instruction_set_import_name))
+    {
+    }
+};
+
+struct Extension_instruction_set
+{
+    std::string instruction_set_name;
+    std::string import_name;
+    Copyright copyright;
+    std::size_t version;
+    std::size_t revision;
+    Instructions instructions;
+    Extension_instruction_set(std::string instruction_set_name,
+                              std::string import_name,
+                              Copyright copyright,
+                              std::size_t version,
+                              std::size_t revision,
+                              Instructions instructions)
+        : instruction_set_name(std::move(instruction_set_name)),
+          import_name(std::move(import_name)),
+          copyright(std::move(copyright)),
+          version(version),
+          revision(revision),
+          instructions(std::move(instructions))
+    {
+    }
+    static constexpr const char *json_file_name_prefix = "extinst.";
+    static constexpr const char *json_file_name_suffix = ".grammar.json";
+    static util::optional<std::string> get_import_name_from_instruction_set_name(
+        util::string_view instruction_set_name);
+    Json_file to_json() const;
+    template <typename Fn>
+    void visit(Fn fn) const
+    {
+        fn(*this);
+        copyright.visit(fn);
+        instructions.visit(fn);
+    }
+};
+
 struct Top_level
 {
+    static constexpr const char *core_grammar_json_file_name = "spirv.core.grammar.json";
     Copyright copyright;
     std::uint32_t magic_number;
     std::size_t major_version;
@@ -484,23 +536,26 @@ struct Top_level
     std::size_t revision;
     Instructions instructions;
     Operand_kinds operand_kinds;
+    std::vector<Extension_instruction_set> extension_instruction_sets;
     Top_level(Copyright copyright,
               std::uint32_t magic_number,
               std::size_t major_version,
               std::size_t minor_version,
               std::size_t revision,
               Instructions instructions,
-              Operand_kinds operand_kinds)
+              Operand_kinds operand_kinds,
+              std::vector<Extension_instruction_set> extension_instruction_sets)
         : copyright(std::move(copyright)),
           magic_number(magic_number),
           major_version(major_version),
           minor_version(minor_version),
           revision(revision),
           instructions(std::move(instructions)),
-          operand_kinds(std::move(operand_kinds))
+          operand_kinds(std::move(operand_kinds)),
+          extension_instruction_sets(std::move(extension_instruction_sets))
     {
     }
-    json::ast::Value to_json() const;
+    std::vector<Json_file> to_json() const;
     template <typename Fn>
     void visit(Fn fn) const
     {
@@ -508,6 +563,8 @@ struct Top_level
         copyright.visit(fn);
         instructions.visit(fn);
         operand_kinds.visit(fn);
+        for(auto &extension_instruction_set : extension_instruction_sets)
+            extension_instruction_set.visit(fn);
     }
 };
 }
index c03e25d58976836d59a598122f6bee0a85a5d61a..e065e8f2fe91d45992e4be6b0e9b74f03ef6e26f 100644 (file)
@@ -34,26 +34,25 @@ namespace generate_spirv_parser
 {
 int generate_spirv_parser_main(int argc, char **argv)
 {
-    std::string file_name;
+    std::string input_directory;
     std::string output_directory;
     if(argc >= 2)
-        file_name = argv[1];
+        input_directory = argv[1];
     if(argc >= 3)
         output_directory = argv[2];
-    if(argc != 3 || (file_name.size() > 1 && file_name[0] == '-')
-       || (output_directory.size() > 1 && output_directory[0] == '-'))
+    if(argc != 3 || input_directory.empty() || input_directory[0] == '-' || output_directory.empty()
+       || output_directory[0] == '-')
     {
-        std::cerr << "usage: " << argv[0] << " <input.json> <output-directory>" << std::endl;
+        std::cerr << "usage: " << argv[0] << " <input-directory> <output-directory>" << std::endl;
         return 1;
     }
     try
     {
-        auto source = file_name == "-" ? json::Source::load_stdin() :
-                                         json::Source::load_file(std::move(file_name));
+        std::shared_ptr<std::vector<ast::Json_file>> required_files; // outside of try so 
         try
         {
-            auto json_in = json::parse(&source);
-            auto ast = parser::parse(json_in.duplicate());
+            required_files = parser::read_required_files(std::move(input_directory));
+            auto ast = parser::parse(std::move(*required_files));
             for(auto *patch : Ast_patches::get_patches())
                 patch->run(ast, &std::cout);
             for(auto &generator : generate::Generators::make_all_generators())
@@ -73,7 +72,7 @@ int generate_spirv_parser_main(int argc, char **argv)
             return 1;
         }
     }
-    catch(std::exception &e)
+    catch(std::runtime_error &e)
     {
         std::cerr << "error: " << e.what() << std::endl;
         return 1;
index f7f7c7610ad9c5e758d3cc5fb7e616c72f70c156..f48eb1eccad53a1c9571d673262dee6fb1c7c267 100644 (file)
  */
 #include "parser.h"
 #include "util/optional.h"
+#include "util/string_view.h"
 #include <sstream>
 #include <limits>
 #include <iostream>
 #include <cstdlib>
+#include <list>
 
 namespace vulkan_cpu
 {
@@ -248,10 +250,10 @@ std::string parse_identifier_string(json::ast::Value value,
     for(char ch : string_value.value)
         if(!is_identifier_continue(ch))
             throw Parse_error(
-        value.location,
-        parent_path_builder->path(),
-        std::string(name)
-            + ": invalid identifier in string: character is not a letter, digit, or underline");
+                value.location,
+                parent_path_builder->path(),
+                std::string(name)
+                + ": invalid identifier in string: character is not a letter, digit, or underline");
     return std::move(string_value.value);
 }
 
@@ -735,14 +737,157 @@ ast::Instructions parse_instructions(json::ast::Value value,
     }
     return ast::Instructions(std::move(instructions));
 }
-}
 
-ast::Top_level parse(json::ast::Value &&top_level_value)
-{
+ast::Extension_instruction_set parse_extension_instruction_set(json::ast::Value top_level_value,
+                                                               std::string file_name,
+                                                               std::string import_name)
+{
+    util::string_view file_name_prefix = ast::Extension_instruction_set::json_file_name_prefix;
+    util::string_view file_name_suffix = ast::Extension_instruction_set::json_file_name_suffix;
+    if(file_name.size() <= file_name_prefix.size() + file_name_suffix.size()
+       || util::string_view(file_name).compare(0, file_name_prefix.size(), file_name_prefix) != 0
+       || util::string_view(file_name).compare(
+              file_name.size() - file_name_suffix.size(), file_name_suffix.size(), file_name_suffix)
+              != 0)
+        throw Parse_error(top_level_value.location, {}, "file name is unrecognizable");
+    auto instruction_set_name = std::move(file_name);
+    instruction_set_name.erase(instruction_set_name.size() - file_name_suffix.size(), file_name_suffix.size());
+    instruction_set_name.erase(0, file_name_prefix.size());
     if(top_level_value.get_value_kind() != json::ast::Value_kind::object)
         throw Parse_error(top_level_value.location, {}, "top level value is not an object");
     auto &top_level_object = top_level_value.get_object();
     util::optional<ast::Copyright> copyright;
+    util::optional<std::size_t> version;
+    util::optional<std::size_t> revision;
+    util::optional<ast::Instructions> instructions;
+    for(auto &entry : top_level_object.values)
+    {
+        const auto &key = std::get<0>(entry);
+        auto &entry_value = std::get<1>(entry);
+        Path_builder<std::string> path_builder(&key, nullptr);
+        if(key == "copyright")
+        {
+            copyright = parse_copyright(std::move(entry_value), &path_builder);
+        }
+        else if(key == "version")
+        {
+            version = parse_integer<std::size_t>(entry_value, &path_builder, "version");
+        }
+        else if(key == "revision")
+        {
+            revision = parse_integer<std::size_t>(entry_value, &path_builder, "revision");
+        }
+        else if(key == "instructions")
+        {
+            instructions = parse_instructions(std::move(entry_value), &path_builder);
+        }
+        else
+        {
+            throw Parse_error(entry_value.location, path_builder.path(), "unknown key");
+        }
+    }
+    return ast::Extension_instruction_set(
+        std::move(instruction_set_name),
+        std::move(import_name),
+        get_value_or_throw_parse_error(
+            std::move(copyright), top_level_value.location, nullptr, "missing copyright"),
+        get_value_or_throw_parse_error(
+            version, top_level_value.location, nullptr, "missing version"),
+        get_value_or_throw_parse_error(
+            revision, top_level_value.location, nullptr, "missing revision"),
+        get_value_or_throw_parse_error(
+            std::move(instructions), top_level_value.location, nullptr, "missing instructions"));
+}
+}
+
+std::shared_ptr<std::vector<ast::Json_file>> read_required_files(
+    const util::filesystem::path &dir_path)
+{
+    struct Result_holder
+    {
+        std::vector<ast::Json_file> retval;
+        std::list<json::Source> sources;
+    };
+    auto result_holder = std::make_shared<Result_holder>();
+    auto retval =
+        std::shared_ptr<std::vector<ast::Json_file>>(result_holder, &result_holder->retval);
+    auto &sources = result_holder->sources;
+    retval->push_back(ast::Json_file(
+        ast::Top_level::core_grammar_json_file_name, json::ast::Value({}, nullptr), {}));
+    util::string_view extension_grammar_prefix =
+        ast::Extension_instruction_set::json_file_name_prefix;
+    util::string_view extension_grammar_suffix =
+        ast::Extension_instruction_set::json_file_name_suffix;
+    for(auto &entry : util::filesystem::directory_iterator(dir_path))
+    {
+        auto filename = entry.path().filename().string();
+        if(filename == ast::Top_level::core_grammar_json_file_name)
+        {
+            // already added; just check file type
+        }
+        else if(filename.size() > extension_grammar_prefix.size() + extension_grammar_suffix.size()
+                && util::string_view(filename)
+                           .compare(0, extension_grammar_prefix.size(), extension_grammar_prefix)
+                       == 0
+                && util::string_view(filename)
+                           .compare(filename.size() - extension_grammar_suffix.size(),
+                                    extension_grammar_suffix.size(),
+                                    extension_grammar_suffix)
+                       == 0)
+        {
+            util::string_view instruction_set_name = filename;
+            instruction_set_name.remove_prefix(extension_grammar_prefix.size());
+            instruction_set_name.remove_suffix(extension_grammar_suffix.size());
+            auto import_name =
+                ast::Extension_instruction_set::get_import_name_from_instruction_set_name(
+                    instruction_set_name);
+            if(!import_name)
+            {
+                std::cerr << "Warning: unknown extended instruction set grammar file -- ignored: "
+                          << entry.path() << std::endl;
+                continue;
+            }
+            retval->push_back(ast::Json_file(
+                std::move(filename), json::ast::Value({}, nullptr), std::move(*import_name)));
+        }
+        else
+            continue;
+        if(!entry.is_regular_file())
+            throw Parse_error({}, {}, "file is not a regular file: " + entry.path().string());
+    }
+    for(auto &file : *retval)
+    {
+        sources.push_back(json::Source::load_file(dir_path / file.file_name));
+        auto &source = sources.back();
+        file.json = json::parse(&source);
+    }
+    return retval;
+}
+
+ast::Top_level parse(std::vector<ast::Json_file> &&json_files)
+{
+    util::optional<json::ast::Value> top_level_value;
+    std::vector<ast::Extension_instruction_set> extension_instruction_sets;
+    if(!json_files.empty())
+        extension_instruction_sets.reserve(json_files.size() - 1);
+    for(auto &file : json_files)
+    {
+        if(file.extension_instruction_set_import_name)
+            extension_instruction_sets.push_back(parse_extension_instruction_set(
+                std::move(file.json),
+                std::move(file.file_name),
+                std::move(*file.extension_instruction_set_import_name)));
+        else if(top_level_value)
+            throw Parse_error(top_level_value->location, {}, "multiple core grammar files");
+        else
+            top_level_value = std::move(file.json);
+    }
+    if(!top_level_value)
+        throw Parse_error(top_level_value->location, {}, "no core grammar file");
+    if(top_level_value->get_value_kind() != json::ast::Value_kind::object)
+        throw Parse_error(top_level_value->location, {}, "top level value is not an object");
+    auto &top_level_object = top_level_value->get_object();
+    util::optional<ast::Copyright> copyright;
     util::optional<std::uint32_t> magic_number;
     util::optional<std::size_t> major_version;
     util::optional<std::size_t> minor_version;
@@ -790,19 +935,20 @@ ast::Top_level parse(json::ast::Value &&top_level_value)
     }
     return ast::Top_level(
         get_value_or_throw_parse_error(
-            std::move(copyright), top_level_value.location, nullptr, "missing copyright"),
+            std::move(copyright), top_level_value->location, nullptr, "missing copyright"),
         get_value_or_throw_parse_error(
-            magic_number, top_level_value.location, nullptr, "missing magic_number"),
+            magic_number, top_level_value->location, nullptr, "missing magic_number"),
         get_value_or_throw_parse_error(
-            major_version, top_level_value.location, nullptr, "missing major_version"),
+            major_version, top_level_value->location, nullptr, "missing major_version"),
         get_value_or_throw_parse_error(
-            minor_version, top_level_value.location, nullptr, "missing minor_version"),
+            minor_version, top_level_value->location, nullptr, "missing minor_version"),
         get_value_or_throw_parse_error(
-            revision, top_level_value.location, nullptr, "missing revision"),
+            revision, top_level_value->location, nullptr, "missing revision"),
         get_value_or_throw_parse_error(
-            std::move(instructions), top_level_value.location, nullptr, "missing instructions"),
+            std::move(instructions), top_level_value->location, nullptr, "missing instructions"),
         get_value_or_throw_parse_error(
-            std::move(operand_kinds), top_level_value.location, nullptr, "missing operand_kinds"));
+            std::move(operand_kinds), top_level_value->location, nullptr, "missing operand_kinds"),
+        std::move(extension_instruction_sets));
 }
 
 #if 0
@@ -815,7 +961,7 @@ void test_fn()
         std::size_t path_index = 0;
         Path_builder<std::size_t> path_builder(&path_index, nullptr);
         std::cout << parse_hex_integer_string<std::uint32_t>(
-                         json::ast::Value({}, "0x1234"), &path_builder, "test", 1, 8)
+                      json::ast::Value({}, "0x1234"), &path_builder, "test", 1, 8)
                   << std::endl;
     }
     catch(std::exception &e)
index acf2457e08c08225bde7df60f0b11990a726021e..dd2a21e639f2f49f7a876e2fb07dec7117d09208 100644 (file)
@@ -32,6 +32,7 @@
 #include "util/variant.h"
 #include "json/json.h"
 #include "json/parser.h"
+#include "util/filesystem.h"
 
 namespace vulkan_cpu
 {
@@ -110,7 +111,9 @@ public:
     }
 };
 
-ast::Top_level parse(json::ast::Value &&top_level_value);
+std::shared_ptr<std::vector<ast::Json_file>> read_required_files(const util::filesystem::path &dir_path);
+
+ast::Top_level parse(std::vector<ast::Json_file> &&json_files);
 }
 }
 }
index f85af006040ea6aa402ef12348a0cf4362011643..e1fefdf08bab330ef054a4a8278dbb3f098665d3 100644 (file)
@@ -26,14 +26,19 @@ set(spirv_parser_generated_include_dir ${CMAKE_CURRENT_BINARY_DIR}/generated)
 set(spirv_parser_generated_dir ${spirv_parser_generated_include_dir}/spirv)
 set(spirv_parser_sources ${spirv_parser_generated_dir}/parser.cpp ${spirv_parser_generated_dir}/spirv.cpp)
 set(spirv_parser_headers ${spirv_parser_generated_dir}/parser.h ${spirv_parser_generated_dir}/spirv.h)
-set(spirv_core_grammar_json ${CMAKE_CURRENT_SOURCE_DIR}/../khronos-spirv/spirv.core.grammar.json)
+set(spirv_grammar_json_dir ${CMAKE_CURRENT_SOURCE_DIR}/../khronos-spirv)
+set(spirv_core_grammar_json ${spirv_grammar_json_dir}/spirv.core.grammar.json)
+file(GLOB spirv_extension_instruction_set_json_files ${spirv_grammar_json_dir}/extinst.*.grammar.json)
 message(STATUS "spirv_parser_generated_dir: " ${spirv_parser_generated_dir})
+foreach(i ${spirv_extension_instruction_set_json_files})
+    message(STATUS "spirv_extension_instruction_set_json_files: " ${i})
+endforeach(i)
 
 add_custom_command(OUTPUT ${spirv_parser_sources} ${spirv_parser_headers}
                    COMMAND ${CMAKE_COMMAND} -E make_directory ${spirv_parser_generated_dir}
-                   COMMAND ${CMAKE_COMMAND} -E chdir ${spirv_parser_generated_include_dir} $<TARGET_FILE:generate_spirv_parser> ${spirv_core_grammar_json} spirv
+                   COMMAND ${CMAKE_COMMAND} -E chdir ${spirv_parser_generated_include_dir} $<TARGET_FILE:generate_spirv_parser> ${spirv_grammar_json_dir} spirv
                    MAIN_DEPENDENCY ${spirv_core_grammar_json}
-                   DEPENDS $<TARGET_FILE:generate_spirv_parser>
+                   DEPENDS $<TARGET_FILE:generate_spirv_parser> ${spirv_extension_instruction_set_json_files}
                    VERBATIM
                    COMMENT "Generating SPIR-V Parser")
 set(sources ${sources} ${spirv_parser_sources})