# SOFTWARE.
#
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
-set(sources generate_spirv_parser.cpp)
+set(sources ast.cpp
+ generate_spirv_parser.cpp
+ parser.cpp)
add_executable(generate_spirv_parser ${sources})
target_link_libraries(generate_spirv_parser util json)
--- /dev/null
+/*
+* Copyright 2017 Jacob Lifshay
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all
+* copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*
+*/
+#include "ast.h"
--- /dev/null
+/*
+* Copyright 2017 Jacob Lifshay
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all
+* copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*
+*/
+
+#ifndef GENERATE_SPIRV_PARSER_AST_H_
+#define GENERATE_SPIRV_PARSER_AST_H_
+
+#include "../json/json.h"
+#include <cstdint>
+
+namespace vulkan_cpu
+{
+namespace generate_spirv_parser
+{
+namespace ast
+{
+struct copyright
+{
+ json::ast::array value;
+ copyright() : value()
+ {
+ }
+ explicit copyright(json::ast::array value) noexcept : value(std::move(value))
+ {
+ }
+};
+
+struct top_level
+{
+ copyright copyright;
+ std::uint32_t magic_number;
+ std::size_t major_version;
+ std::size_t minor_version;
+#warning finish adding members
+ top_level(ast::copyright copyright,
+ std::uint32_t magic_number,
+ std::size_t major_version,
+ std::size_t minor_version)
+ : copyright(std::move(copyright)),
+ magic_number(magic_number),
+ major_version(major_version),
+ minor_version(minor_version)
+ {
+ }
+};
+}
+}
+}
+
+#endif /* GENERATE_SPIRV_PARSER_AST_H_ */
#include <iostream>
#include "../json/json.h"
#include "../json/parser.h"
+#include "parser.h"
+#include "../util/optional.h"
namespace vulkan_cpu
{
namespace generate_spirv_parser
{
-namespace ast = json::ast;
-
int generate_spirv_parser_main(int argc, char **argv)
{
std::string file_name;
}
try
{
- const auto source = file_name == "-" ? json::source::load_stdin() :
- json::source::load_file(std::move(file_name));
- auto value = json::parse(&source);
- json::write(std::cout, value, json::write_options::pretty());
- std::cout << std::endl;
- }
- catch(json::parse_error &e)
- {
- std::cerr << "error: " << e.what() << std::endl;
- return 1;
+ auto source = file_name == "-" ? json::source::load_stdin() :
+ json::source::load_file(std::move(file_name));
+ try
+ {
+ auto ast = parser::parse(json::parse(&source));
+ }
+ catch(json::parse_error &e)
+ {
+ std::cerr << "error: " << e.what() << std::endl;
+ return 1;
+ }
+ catch(parser::parse_error &e)
+ {
+ std::cerr << "error: " << e.what() << std::endl;
+ return 1;
+ }
}
catch(std::exception &e)
{
--- /dev/null
+/*
+* Copyright 2017 Jacob Lifshay
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all
+* copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*
+*/
+#include "parser.h"
+#include "../util/optional.h"
+#include <sstream>
+#include <limits>
+
+namespace vulkan_cpu
+{
+namespace generate_spirv_parser
+{
+namespace parser
+{
+std::string path::to_string() const
+{
+ std::ostringstream ss;
+ ss << "root";
+ for(auto &e : elements)
+ {
+ ss << '[';
+ if(util::holds_alternative<std::size_t>(e))
+ {
+ ss << util::get<std::size_t>(e);
+ }
+ else
+ {
+ json::ast::string_value::write(ss, util::get<std::string>(e));
+ }
+ ss << ']';
+ }
+ return ss.str();
+}
+
+namespace
+{
+template <typename Value, std::size_t N>
+Value get_value_or_throw_parse_error(util::optional<Value> value,
+ path_builder_base *path_builder,
+ const char(&message)[N])
+{
+ if(value)
+ return std::move(*value);
+ throw parse_error(path_builder ? path_builder->path() : path{}, message);
+}
+
+ast::copyright parse_copyright(json::ast::value value, const path_builder_base *parent_path_builder)
+{
+ if(json::ast::get_value_kind(value) != json::ast::value_kind::array)
+ throw parse_error(parent_path_builder->path(), "copyright is not an array");
+ auto ©right_array =
+ static_cast<json::ast::array &>(*util::get<json::ast::composite_value_pointer>(value));
+ for(std::size_t index = 0; index < copyright_array.values.size(); index++)
+ {
+ path_builder<std::size_t> path_builder(&index, parent_path_builder);
+ auto &element = copyright_array.values[index];
+ if(json::ast::get_value_kind(element) != json::ast::value_kind::string)
+ throw parse_error(parent_path_builder->path(),
+ "copyright array's element is not a string");
+ }
+ return ast::copyright(std::move(copyright_array));
+}
+
+template <typename T>
+T parse_integer(const json::ast::value &value,
+ const path_builder_base *parent_path_builder,
+ const char *name)
+{
+ if(json::ast::get_value_kind(value) != json::ast::value_kind::number)
+ throw parse_error(parent_path_builder->path(), std::string(name) + " is not a number");
+ auto number_value = util::get<json::ast::number_value>(value);
+ T retval = number_value.value;
+ if(retval != number_value.value) // not an exact value
+ throw parse_error(parent_path_builder->path(), std::string(name) + " is not an integer");
+ return retval;
+}
+
+constexpr int get_digit_value(int ch, unsigned base) noexcept
+{
+ unsigned retval{};
+ if(ch >= '0' && ch <= '9')
+ retval = ch - '0';
+ else if(ch >= 'a' && ch <= 'z')
+ retval = ch - 'a' + 0xA;
+ else if(ch >= 'A' && ch <= 'Z')
+ retval = ch - 'A' + 0xA;
+ else
+ return -1;
+ if(retval >= base)
+ return -1;
+ return retval;
+}
+
+template <typename T>
+T parse_hex_integer_string(const json::ast::value &value,
+ const path_builder_base *parent_path_builder,
+ const char *name,
+ std::size_t min_length,
+ std::size_t max_length)
+{
+ if(json::ast::get_value_kind(value) != json::ast::value_kind::string)
+ throw parse_error(parent_path_builder->path(), std::string(name) + " is not a string");
+ auto &string_value = util::get<json::ast::string_value>(value);
+ constexpr std::size_t hex_number_prefix_length = 2; // std::strlen("0x")
+ if(string_value.value.size() < hex_number_prefix_length || string_value.value[0] != '0'
+ || (string_value.value[1] != 'x' && string_value.value[1] != 'X'))
+ throw parse_error(parent_path_builder->path(),
+ std::string(name) + " is not a valid hex number in a string");
+ constexpr T max_value = std::numeric_limits<T>::max();
+ constexpr unsigned base = 0x10;
+ T retval = 0;
+ std::size_t digit_count = 0;
+ for(std::size_t i = hex_number_prefix_length; i < string_value.value.size(); i++)
+ {
+ digit_count++;
+ char ch = string_value.value[i];
+ int digit = get_digit_value(ch, base);
+ if(digit < 0)
+ throw parse_error(parent_path_builder->path(),
+ std::string(name) + ": not a valid hex digit");
+ if(digit_count > max_length)
+ throw parse_error(parent_path_builder->path(),
+ std::string(name) + " has too many digits");
+ if(retval > max_value / base
+ || (retval = max_value / base && static_cast<unsigned>(digit) > max_value % base))
+ throw parse_error(parent_path_builder->path(), std::string(name) + ": value too big");
+ retval *= base;
+ retval += digit;
+ }
+ if(digit_count < min_length)
+ throw parse_error(parent_path_builder->path(),
+ std::string(name) + " doesn't have enough digits");
+ return retval;
+}
+}
+
+ast::top_level parse(json::ast::value &&top_level_value)
+{
+ if(json::ast::get_value_kind(top_level_value) != json::ast::value_kind::object)
+ throw parse_error({}, "top level value is not an object");
+ auto &top_level_object = static_cast<const json::ast::object &>(
+ *util::get<json::ast::composite_value_pointer>(top_level_value));
+ 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;
+#warning finish adding ast::top_level members
+ 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 == "magic_number")
+ {
+ magic_number = parse_hex_integer_string<std::uint32_t>(
+ entry_value, &path_builder, "magic_number", 1, 8);
+ }
+ else if(key == "major_version")
+ {
+ major_version = parse_integer<std::size_t>(entry_value, &path_builder, "major_version");
+ }
+ else if(key == "minor_version")
+ {
+ minor_version = parse_integer<std::size_t>(entry_value, &path_builder, "minor_version");
+ }
+ else
+ {
+ throw parse_error(path_builder.path(), "unknown key");
+ }
+ }
+ return ast::top_level(
+ get_value_or_throw_parse_error(std::move(copyright), nullptr, "missing copyright"),
+ get_value_or_throw_parse_error(magic_number, nullptr, "missing magic_number"),
+ get_value_or_throw_parse_error(major_version, nullptr, "missing major_version"),
+ get_value_or_throw_parse_error(minor_version, nullptr, "missing minor_version"));
+}
+}
+}
+}
\ No newline at end of file
--- /dev/null
+/*
+* Copyright 2017 Jacob Lifshay
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all
+* copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+* SOFTWARE.
+*
+*/
+
+#ifndef GENERATE_SPIRV_PARSER_PARSER_H_
+#define GENERATE_SPIRV_PARSER_PARSER_H_
+
+#include "ast.h"
+#include <stdexcept>
+#include <cassert>
+#include <string>
+#include <vector>
+#include "../util/variant.h"
+#include "../json/json.h"
+
+namespace vulkan_cpu
+{
+namespace generate_spirv_parser
+{
+namespace parser
+{
+struct path
+{
+ typedef util::variant<std::size_t, std::string> element;
+ std::vector<element> elements;
+ path() : elements()
+ {
+ }
+ path(std::vector<element> elements) : elements(std::move(elements))
+ {
+ }
+ path(std::initializer_list<element> elements) : elements(elements)
+ {
+ }
+ std::string to_string() const;
+};
+
+struct path_builder_base
+{
+ path_builder_base(const path_builder_base &) = delete;
+ path_builder_base &operator=(const path_builder_base &) = delete;
+ virtual ~path_builder_base() = default;
+ const path_builder_base *const parent;
+ const std::size_t element_count;
+ explicit path_builder_base(const path_builder_base *parent) noexcept
+ : parent(parent),
+ element_count(parent ? parent->element_count + 1 : 1)
+ {
+ }
+ virtual path::element get_element() const = 0;
+ path path() const
+ {
+ std::vector<path::element> elements;
+ elements.resize(element_count);
+ const path_builder_base *node = this;
+ for(std::size_t i = 0, j = element_count - 1; i < element_count;
+ i++, j--, node = node->parent)
+ {
+ assert(node);
+ elements[j] = node->get_element();
+ }
+ assert(!node);
+ return std::move(elements);
+ }
+};
+
+template <typename T>
+struct path_builder final : public path_builder_base
+{
+ const T *value;
+ path_builder(const T *value, const path_builder_base *parent) noexcept
+ : path_builder_base(parent),
+ value(value)
+ {
+ }
+ virtual path::element get_element() const override
+ {
+ return *value;
+ }
+};
+
+class parse_error : public std::runtime_error
+{
+public:
+ path path;
+ parse_error(parser::path path, const std::string &message)
+ : runtime_error("at " + path.to_string() + ": " + message), path(std::move(path))
+ {
+ }
+};
+
+ast::top_level parse(json::ast::value &&top_level_value);
+}
+}
+}
+
+#endif /* GENERATE_SPIRV_PARSER_PARSER_H_ */
namespace ast
{
+enum class value_kind
+{
+ null,
+ boolean,
+ string,
+ number,
+ object,
+ array
+};
+
struct null_value final
{
constexpr null_value() noexcept = default;
{
return *this;
}
+ constexpr value_kind get_value_kind() const noexcept
+ {
+ return value_kind::null;
+ }
};
struct boolean_value final
{
return *this;
}
+ constexpr value_kind get_value_kind() const noexcept
+ {
+ return value_kind::boolean;
+ }
};
struct string_value final
{
}
static void write(std::ostream &os, const std::string &value, write_state &state);
+ static void write(std::ostream &os, const std::string &value)
+ {
+ write_state state(write_options::defaults());
+ write(os, value, state);
+ }
void write(std::ostream &os, write_state &state) const
{
write(os, value, state);
{
return *this;
}
+ constexpr value_kind get_value_kind() const noexcept
+ {
+ return value_kind::string;
+ }
};
struct number_value final
{
return *this;
}
+ constexpr value_kind get_value_kind() const noexcept
+ {
+ return value_kind::number;
+ }
};
struct composite_value;
{
return *value;
}
+ const std::shared_ptr<composite_value> &get() const &noexcept
+ {
+ return value;
+ }
+ std::shared_ptr<composite_value> get() && noexcept
+ {
+ std::shared_ptr<composite_value> retval = nullptr;
+ retval.swap(value);
+ return retval;
+ }
};
typedef util::
{
return duplicate();
}
+ virtual value_kind get_value_kind() const noexcept = 0;
};
inline value duplicate(const value &v)
}
return std::make_shared<object>(std::move(new_values));
}
+ value_kind get_value_kind() const noexcept override
+ {
+ return value_kind::object;
+ }
};
struct array final : public composite_value
new_values.emplace_back(ast::duplicate(value));
return std::make_shared<array>(std::move(new_values));
}
+ value_kind get_value_kind() const noexcept override
+ {
+ return value_kind::array;
+ }
};
+
+inline value_kind get_value_kind(const value &v) noexcept
+{
+ return util::visit(
+ [&](const auto &v) -> value_kind
+ {
+ return v->get_value_kind();
+ },
+ v);
+}
}
inline void write(std::ostream &os, const ast::value &v, write_state &state)
util::visit(
[&](const auto &v) -> void
{
- return v->write(os, state);
+ v->write(os, state);
},
v);
}
# SOFTWARE.
#
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
-set(sources spirv.cpp)
+
+set(spirv_parser_generated_include_dir ${CMAKE_CURRENT_BINARY_DIR}/src)
+set(spirv_parser_generated_dir ${spirv_parser_generated_include_dir}/spirv)
+set(spirv_parser_source ${spirv_parser_generated_dir}/parser.cpp)
+set(spirv_parser_header ${spirv_parser_generated_dir}/parser.h)
+set(spirv_core_grammar_json ${CMAKE_CURRENT_SOURCE_DIR}/../khronos-spirv/spirv.core.grammar.json)
+
+add_custom_command(OUTPUT ${spirv_parser_source} ${spirv_parser_header}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${spirv_parser_generated_dir}
+ COMMAND ${CMAKE_COMMAND} -E chdir ${spirv_parser_generated_dir} $<TARGET_FILE:generate_spirv_parser> ${spirv_core_grammar_json}
+ MAIN_DEPENDENCY ${spirv_core_grammar_json}
+ DEPENDS $<TARGET_FILE:generate_spirv_parser>
+ VERBATIM
+ COMMENT "Generating SPIR-V Parser")
+
+set(sources spirv.cpp ${spirv_parser_source})
add_library(spirv STATIC ${sources})
-target_link_libraries(spirv util)
\ No newline at end of file
+target_link_libraries(spirv util)
+target_include_directories(spirv PUBLIC ${spirv_parser_generated_include_dir})
\ No newline at end of file