{
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)
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;
}
}
}
}
};
+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;
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
{
copyright.visit(fn);
instructions.visit(fn);
operand_kinds.visit(fn);
+ for(auto &extension_instruction_set : extension_instruction_sets)
+ extension_instruction_set.visit(fn);
}
};
}
*/
#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
{
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);
}
}
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;
}
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
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)