*
*/
#include "ast.h"
+
+namespace vulkan_cpu
+{
+namespace generate_spirv_parser
+{
+namespace ast
+{
+namespace
+{
+std::string to_hex_string(std::uint32_t v)
+{
+ return json::ast::Number_value::append_unsigned_integer_to_string(v, "0x", 0x10, 8);
+}
+
+constexpr json::Location make_empty_location() noexcept
+{
+ // use function to make empty location so it will be easy to find all occurrences if location
+ // info is added to ast
+ return {};
+}
+}
+
+json::ast::Value Copyright::to_json() const
+{
+ json::ast::Array retval;
+ retval.values.reserve(lines.size());
+ for(auto &line : lines)
+ retval.values.push_back(json::ast::Value(make_empty_location(), line));
+ return json::ast::Value(make_empty_location(), std::move(retval));
+}
+
+json::ast::Value Instructions::to_json() const
+{
+ json::ast::Array retval;
+#warning finish
+ return json::ast::Value(make_empty_location(), std::move(retval));
+}
+
+json::ast::Value Operand_kinds::Operand_kind::Enumerants::Enumerant::to_json() const
+{
+ json::ast::Object retval;
+#warning finish
+ return json::ast::Value(make_empty_location(), std::move(retval));
+}
+
+json::ast::Value Operand_kinds::Operand_kind::Enumerants::to_json() const
+{
+ json::ast::Array retval;
+ retval.values.reserve(enumerants.size());
+ for(auto &enumerant : enumerants)
+ retval.values.push_back(enumerant.to_json());
+ return json::ast::Value(make_empty_location(), std::move(retval));
+}
+
+json::ast::Value Operand_kinds::Operand_kind::Doc::to_json() const
+{
+ return json::ast::Value(make_empty_location(), value);
+}
+
+json::ast::Value Operand_kinds::Operand_kind::Bases::to_json() const
+{
+ json::ast::Array retval;
+ retval.values.reserve(values.size());
+ for(auto &value : values)
+ retval.values.push_back(json::ast::Value(make_empty_location(), value));
+ return json::ast::Value(make_empty_location(), std::move(retval));
+}
+
+json::ast::Value Operand_kinds::Operand_kind::to_json() const
+{
+ json::ast::Object retval;
+ retval.values["category"] =
+ json::ast::Value(make_empty_location(), get_json_name_from_category(category));
+ retval.values["kind"] = json::ast::Value(make_empty_location(), kind);
+ retval.values[get_value_json_key_name_from_category(category)] = util::visit(
+ [&](auto &v) -> json::ast::Value
+ {
+ return v.to_json();
+ },
+ value);
+ return json::ast::Value(make_empty_location(), std::move(retval));
+}
+
+json::ast::Value Operand_kinds::to_json() const
+{
+ json::ast::Array retval;
+ retval.values.reserve(operand_kinds.size());
+ for(auto &operand_kind : operand_kinds)
+ retval.values.push_back(operand_kind.to_json());
+ return json::ast::Value(make_empty_location(), std::move(retval));
+}
+
+json::ast::Value Top_level::to_json() const
+{
+ json::ast::Object retval;
+ retval.values["copyright"] = copyright.to_json();
+ retval.values["magic_number"] =
+ json::ast::Value(make_empty_location(), to_hex_string(magic_number));
+ retval.values["major_version"] = json::ast::Value(
+ make_empty_location(), json::ast::Number_value::unsigned_integer_to_string(major_version));
+ retval.values["minor_version"] = json::ast::Value(
+ make_empty_location(), json::ast::Number_value::unsigned_integer_to_string(minor_version));
+ retval.values["revision"] = json::ast::Value(
+ make_empty_location(), json::ast::Number_value::unsigned_integer_to_string(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));
+}
+}
+}
+}
#include "../json/json.h"
#include <cstdint>
+#include <vector>
+#include <string>
namespace vulkan_cpu
{
{
struct Copyright
{
- json::ast::Array value;
- Copyright() : value()
+ std::vector<std::string> lines;
+ Copyright() : lines()
{
}
- explicit Copyright(json::ast::Array value) noexcept : value(std::move(value))
+ explicit Copyright(std::vector<std::string> lines) noexcept : lines(std::move(lines))
{
}
+ json::ast::Value to_json() const;
};
struct Instructions
{
#warning finish
+ json::ast::Value to_json() const;
};
struct Operand_kinds
{
+ struct Operand_kind
+ {
+ enum class Category
+ {
+ bit_enum,
+ value_enum,
+ id,
+ literal,
+ composite,
+ };
+ Category category;
+ static constexpr const char *get_json_name_from_category(Category category) noexcept
+ {
+ switch(category)
+ {
+ case Category::bit_enum:
+ return "BitEnum";
+ case Category::value_enum:
+ return "ValueEnum";
+ case Category::id:
+ return "Id";
+ case Category::literal:
+ return "Literal";
+ case Category::composite:
+ return "Composite";
+ }
+ return "";
+ }
+ std::string kind;
+ struct Enumerants
+ {
+ static constexpr const char *get_json_key_name() noexcept
+ {
+ return "enumerants";
+ }
+ struct Enumerant
+ {
+#warning finish
+ json::ast::Value to_json() const;
+ };
+ std::vector<Enumerant> enumerants;
+ explicit Enumerants(std::vector<Enumerant> enumerants) noexcept : enumerants(enumerants)
+ {
+ }
+ json::ast::Value to_json() const;
+ };
+ struct Doc
+ {
+ static constexpr const char *get_json_key_name() noexcept
+ {
+ return "doc";
+ }
+ std::string value;
+ json::ast::Value to_json() const;
+ };
+ struct Bases
+ {
+ static constexpr const char *get_json_key_name() noexcept
+ {
+ return "bases";
+ }
+ std::vector<std::string> values;
+ json::ast::Value to_json() const;
+ };
+ typedef util::variant<Enumerants, Doc, Bases> Value;
+ Value value;
+ static bool does_category_match_value(Category category, const Value &value) noexcept
+ {
+ switch(category)
+ {
+ case Category::bit_enum:
+ case Category::value_enum:
+ return util::holds_alternative<Enumerants>(value);
+ case Category::id:
+ case Category::literal:
+ return util::holds_alternative<Doc>(value);
+ case Category::composite:
+ return util::holds_alternative<Bases>(value);
+ }
+ return false;
+ }
+ static constexpr const char *get_value_json_key_name_from_category(
+ Category category) noexcept
+ {
+ switch(category)
+ {
+ case Category::bit_enum:
+ case Category::value_enum:
+ return Enumerants::get_json_key_name();
+ case Category::id:
+ case Category::literal:
+ return Doc::get_json_key_name();
+ case Category::composite:
+ return Bases::get_json_key_name();
+ }
+ return "";
+ }
#warning finish
+ Operand_kind(Category category, std::string kind, Value value) noexcept
+ : category(category),
+ kind(kind),
+ value(std::move(value))
+ {
+ }
+ json::ast::Value to_json() const;
+ };
+ std::vector<Operand_kind> operand_kinds;
+ explicit Operand_kinds(std::vector<Operand_kind> operand_kinds) noexcept
+ : operand_kinds(std::move(operand_kinds))
+ {
+ }
+ json::ast::Value to_json() const;
};
struct Top_level
operand_kinds(std::move(operand_kinds))
{
}
+ json::ast::Value to_json() const;
};
}
}
#include "../json/parser.h"
#include "parser.h"
#include "../util/optional.h"
+#include <fstream>
namespace vulkan_cpu
{
try
{
auto ast = parser::parse(json::parse(&source));
+ std::ofstream os("out.json");
+ json::write(os, ast.to_json(), json::Write_options::pretty());
+#warning finish
+ std::cerr << "generate_spirv_parser is not finished being implemented" << std::endl;
+ return 1;
}
- catch(json::Parse_error &e)
+ catch(parser::Parse_error &e)
{
- std::cerr << "error: " << e.what() << std::endl;
+ std::cerr << e.what() << std::endl;
return 1;
}
- catch(parser::Parse_error &e)
+ catch(json::Parse_error &e)
{
- std::cerr << "error: " << e.what() << std::endl;
+ std::cerr << e.what() << std::endl;
return 1;
}
}
namespace
{
-template <typename Value, std::size_t N>
+template <typename Value>
Value get_value_or_throw_parse_error(util::optional<Value> value,
- Path_builder_base *path_builder,
- const char(&message)[N])
+ const json::Location &location,
+ const Path_builder_base *path_builder,
+ const char *message)
{
if(value)
return std::move(*value);
- throw Parse_error(path_builder ? path_builder->path() : Path{}, message);
+ throw Parse_error(location, path_builder ? path_builder->path() : Path{}, message);
}
-ast::Copyright parse_copyright(json::ast::Value value, const Path_builder_base *parent_path_builder)
+template <typename Value>
+Value get_value_or_throw_parse_error(util::optional<Value> value,
+ const json::Location &location,
+ const Path_builder_base *path_builder,
+ const std::string &message)
{
- 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));
+ return get_value_or_throw_parse_error(
+ std::move(value), location, path_builder, message.c_str());
}
-ast::Operand_kinds parse_operand_kinds(json::ast::Value value,
- const Path_builder_base *parent_path_builder)
+template <typename Callback>
+decltype(auto) get_object_member_or_throw_parse_error(const json::Location &object_location,
+ json::ast::Object &object,
+ const Path_builder_base *parent_path_builder,
+ const std::string &name,
+ Callback callback)
{
- if(json::ast::get_value_kind(value) != json::ast::Value_kind::array)
- throw Parse_error(parent_path_builder->path(), "operand_kinds is not an array");
- auto &operand_kinds_array =
- static_cast<json::ast::Array &>(*util::get<json::ast::Composite_value_pointer>(value));
- static_cast<void>(operand_kinds_array);
-#warning finish
- return ast::Operand_kinds();
+ auto iter = object.values.find(name);
+ if(iter == object.values.end())
+ throw Parse_error(object_location,
+ parent_path_builder ? parent_path_builder->path() : Path{},
+ "missing " + name);
+ auto &entry = *iter;
+ auto &key = std::get<0>(entry);
+ auto &entry_value = std::get<1>(entry);
+ const Path_builder<std::string> path_builder(&key, parent_path_builder);
+ return callback(entry_value, &path_builder);
}
-ast::Instructions parse_instructions(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(), "instructions is not an array");
- auto &instructions_array =
- static_cast<json::ast::Array &>(*util::get<json::ast::Composite_value_pointer>(value));
- static_cast<void>(instructions_array);
-#warning finish
- return ast::Instructions();
-}
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);
+ if(value.get_value_kind() != json::ast::Value_kind::number)
+ throw Parse_error(
+ value.location, parent_path_builder->path(), std::string(name) + " is not a number");
+ auto number_value = value.get_number();
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");
+ throw Parse_error(
+ value.location, parent_path_builder->path(), std::string(name) + " is not an integer");
return retval;
}
-constexpr int get_digit_value(int ch, unsigned base) noexcept
+constexpr int get_digit_value(unsigned char ch, unsigned base) noexcept
{
unsigned retval{};
if(ch >= '0' && ch <= '9')
return retval;
}
+constexpr bool is_identifier_start(unsigned char ch) noexcept
+{
+ if(ch >= 'a' && ch <= 'z')
+ return true;
+ if(ch >= 'A' && ch <= 'Z')
+ return true;
+ return ch == '_';
+}
+
+constexpr bool is_identifier_continue(unsigned char ch) noexcept
+{
+ if(ch >= '0' && ch <= '9')
+ return true;
+ return is_identifier_start(ch);
+}
+
template <typename T>
T parse_hex_integer_string(const json::ast::Value &value,
const Path_builder_base *parent_path_builder,
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);
+ if(value.get_value_kind() != json::ast::Value_kind::string)
+ throw Parse_error(
+ value.location, parent_path_builder->path(), std::string(name) + " is not a string");
+ auto &string_value = value.get_string();
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(),
+ throw Parse_error(value.location,
+ 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;
char ch = string_value.value[i];
int digit = get_digit_value(ch, base);
if(digit < 0)
- throw Parse_error(parent_path_builder->path(),
+ throw Parse_error(value.location,
+ parent_path_builder->path(),
std::string(name) + ": not a valid hex digit");
if(digit_count > max_length)
- throw Parse_error(parent_path_builder->path(),
+ throw Parse_error(value.location,
+ 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");
+ throw Parse_error(
+ value.location, 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(),
+ throw Parse_error(value.location,
+ parent_path_builder->path(),
std::string(name) + " doesn't have enough digits");
return retval;
}
+
+template <typename Enum>
+struct Enum_value_descriptor
+{
+ const char *name;
+ Enum value;
+ constexpr Enum_value_descriptor(const char *name, Enum value) noexcept : name(name),
+ value(value)
+ {
+ }
+};
+
+template <typename Enum, const char *(*Get_Name)(Enum value), Enum... Values>
+constexpr std::initializer_list<Enum_value_descriptor<Enum>> make_enum_value_descriptors = {
+ {Get_Name(Values), Values}...};
+
+template <typename Enum>
+Enum parse_enum_string(const json::ast::Value &value,
+ const Path_builder_base *parent_path_builder,
+ const char *name,
+ std::initializer_list<Enum_value_descriptor<Enum>> enum_value_descriptors)
+{
+ if(value.get_value_kind() != json::ast::Value_kind::string)
+ throw Parse_error(
+ value.location, parent_path_builder->path(), std::string(name) + " is not a string");
+ auto &string_value = value.get_string();
+ for(auto &descriptor : enum_value_descriptors)
+ {
+ if(string_value.value == descriptor.name)
+ return descriptor.value;
+ }
+ throw Parse_error(
+ value.location, parent_path_builder->path(), std::string(name) + ": unknown value");
+}
+
+std::string parse_identifier_string(json::ast::Value value,
+ const Path_builder_base *parent_path_builder,
+ const char *name)
+{
+ if(value.get_value_kind() != json::ast::Value_kind::string)
+ throw Parse_error(
+ value.location, parent_path_builder->path(), std::string(name) + " is not a string");
+ auto &string_value = value.get_string();
+ if(string_value.value.empty())
+ throw Parse_error(value.location,
+ parent_path_builder->path(),
+ std::string(name) + " must not be an empty string");
+ if(!is_identifier_start(string_value.value[0]))
+ throw Parse_error(
+ value.location,
+ parent_path_builder->path(),
+ std::string(name)
+ + ": invalid identifier in string: must start with letter or underline");
+ 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");
+ return std::move(string_value.value);
+}
+
+ast::Copyright parse_copyright(json::ast::Value value, const Path_builder_base *parent_path_builder)
+{
+ if(value.get_value_kind() != json::ast::Value_kind::array)
+ throw Parse_error(value.location, parent_path_builder->path(), "copyright is not an array");
+ auto ©right_array = value.get_array();
+ std::vector<std::string> lines;
+ lines.reserve(copyright_array.values.size());
+ 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(element.get_value_kind() != json::ast::Value_kind::string)
+ throw Parse_error(element.location,
+ parent_path_builder->path(),
+ "copyright array's element is not a string");
+ lines.push_back(std::move(element.get_string().value));
+ }
+ return ast::Copyright(std::move(lines));
+}
+
+ast::Operand_kinds::Operand_kind::Enumerants::Enumerant
+ parse_operand_kinds_operand_kind_enumerants_enumerant(
+ json::ast::Value value, const Path_builder_base *parent_path_builder)
+{
+ if(value.get_value_kind() != json::ast::Value_kind::object)
+ throw Parse_error(
+ value.location, parent_path_builder->path(), "enumerant is not an object");
+ auto &enumerant_object = value.get_object();
+ static_cast<void>(enumerant_object);
+#warning finish
+ return ast::Operand_kinds::Operand_kind::Enumerants::Enumerant();
+}
+
+ast::Operand_kinds::Operand_kind::Enumerants parse_operand_kinds_operand_kind_enumerants(
+ json::ast::Value value, const Path_builder_base *parent_path_builder)
+{
+ if(value.get_value_kind() != json::ast::Value_kind::array)
+ throw Parse_error(
+ value.location, parent_path_builder->path(), "enumerants is not an array");
+ auto &enumerants_array = value.get_array();
+ std::vector<ast::Operand_kinds::Operand_kind::Enumerants::Enumerant> enumerants;
+ enumerants.reserve(enumerants_array.values.size());
+ for(std::size_t index = 0; index < enumerants_array.values.size(); index++)
+ {
+ Path_builder<std::size_t> path_builder(&index, parent_path_builder);
+ enumerants.push_back(parse_operand_kinds_operand_kind_enumerants_enumerant(
+ std::move(enumerants_array.values[index]), &path_builder));
+ }
+ return ast::Operand_kinds::Operand_kind::Enumerants(std::move(enumerants));
+}
+
+ast::Operand_kinds::Operand_kind parse_operand_kinds_operand_kind(
+ json::ast::Value value, const Path_builder_base *parent_path_builder)
+{
+ if(value.get_value_kind() != json::ast::Value_kind::object)
+ throw Parse_error(
+ value.location, parent_path_builder->path(), "operand kind is not an object");
+ auto &operand_kind_object = value.get_object();
+ constexpr auto category_name = "category";
+ constexpr auto kind_name = "kind";
+ ast::Operand_kinds::Operand_kind::Category category = get_object_member_or_throw_parse_error(
+ value.location,
+ operand_kind_object,
+ parent_path_builder,
+ category_name,
+ [&](json::ast::Value &entry_value, const Path_builder_base *path_builder)
+ {
+ return parse_enum_string<ast::Operand_kinds::Operand_kind::Category>(
+ std::move(entry_value),
+ path_builder,
+ "category",
+ make_enum_value_descriptors<ast::Operand_kinds::Operand_kind::Category,
+ ast::Operand_kinds::Operand_kind::
+ get_json_name_from_category,
+ ast::Operand_kinds::Operand_kind::Category::bit_enum,
+ ast::Operand_kinds::Operand_kind::Category::value_enum,
+ ast::Operand_kinds::Operand_kind::Category::id,
+ ast::Operand_kinds::Operand_kind::Category::literal,
+ ast::Operand_kinds::Operand_kind::Category::composite>);
+ });
+ std::string kind = get_object_member_or_throw_parse_error(
+ value.location,
+ operand_kind_object,
+ parent_path_builder,
+ kind_name,
+ [&](json::ast::Value &entry_value, const Path_builder_base *path_builder)
+ {
+ return parse_identifier_string(std::move(entry_value), path_builder, kind_name);
+ });
+ util::optional<ast::Operand_kinds::Operand_kind::Value> operand_kind_value;
+ for(auto &entry : operand_kind_object.values)
+ {
+ const auto &key = std::get<0>(entry);
+ auto &entry_value = std::get<1>(entry);
+ Path_builder<std::string> path_builder(&key, parent_path_builder);
+ if(key == ast::Operand_kinds::Operand_kind::get_value_json_key_name_from_category(category))
+ {
+ switch(category)
+ {
+ case ast::Operand_kinds::Operand_kind::Category::bit_enum:
+ case ast::Operand_kinds::Operand_kind::Category::value_enum:
+ operand_kind_value = parse_operand_kinds_operand_kind_enumerants(
+ std::move(entry_value), &path_builder);
+ break;
+ case ast::Operand_kinds::Operand_kind::Category::id:
+ case ast::Operand_kinds::Operand_kind::Category::literal:
+ if(entry_value.get_value_kind() != json::ast::Value_kind::string)
+ throw Parse_error(
+ entry_value.location, path_builder.path(), "doc is not a string");
+ operand_kind_value = ast::Operand_kinds::Operand_kind::Doc{
+ std::move(entry_value.get_string().value)};
+ break;
+ case ast::Operand_kinds::Operand_kind::Category::composite:
+ {
+ if(entry_value.get_value_kind() != json::ast::Value_kind::array)
+ throw Parse_error(
+ entry_value.location, path_builder.path(), "bases is not an array");
+ auto &bases_array = entry_value.get_array();
+ std::vector<std::string> bases;
+ bases.reserve(bases_array.values.size());
+ for(std::size_t i = 0; i < bases_array.values.size(); i++)
+ {
+ Path_builder<std::size_t> path_builder2(&i, &path_builder);
+ auto &entry = bases_array.values[i];
+ if(entry.get_value_kind() != json::ast::Value_kind::string)
+ throw Parse_error(entry_value.location,
+ path_builder.path(),
+ "bases element is not a string");
+ bases.push_back(std::move(entry.get_string().value));
+ }
+ operand_kind_value = ast::Operand_kinds::Operand_kind::Bases{std::move(bases)};
+ break;
+ }
+ }
+ }
+ else if(key != category_name && key != kind_name)
+ {
+ throw Parse_error(entry_value.location, path_builder.path(), "unknown key");
+ }
+ }
+ return ast::Operand_kinds::Operand_kind(
+ category,
+ std::move(kind),
+ get_value_or_throw_parse_error(
+ std::move(operand_kind_value),
+ value.location,
+ parent_path_builder,
+ std::string("missing ")
+ + ast::Operand_kinds::Operand_kind::get_value_json_key_name_from_category(
+ category)));
+}
+
+ast::Operand_kinds parse_operand_kinds(json::ast::Value value,
+ const Path_builder_base *parent_path_builder)
+{
+ if(value.get_value_kind() != json::ast::Value_kind::array)
+ throw Parse_error(
+ value.location, parent_path_builder->path(), "operand_kinds is not an array");
+ auto &operand_kinds_array = value.get_array();
+ std::vector<ast::Operand_kinds::Operand_kind> operand_kinds;
+ operand_kinds.reserve(operand_kinds_array.values.size());
+ for(std::size_t index = 0; index < operand_kinds_array.values.size(); index++)
+ {
+ Path_builder<std::size_t> path_builder(&index, parent_path_builder);
+ operand_kinds.push_back(parse_operand_kinds_operand_kind(
+ std::move(operand_kinds_array.values[index]), &path_builder));
+ }
+ return ast::Operand_kinds(std::move(operand_kinds));
+}
+
+ast::Instructions parse_instructions(json::ast::Value value,
+ const Path_builder_base *parent_path_builder)
+{
+ if(value.get_value_kind() != json::ast::Value_kind::array)
+ throw Parse_error(
+ value.location, parent_path_builder->path(), "instructions is not an array");
+ auto &instructions_array = value.get_array();
+ static_cast<void>(instructions_array);
+#warning finish
+ return ast::Instructions();
+}
}
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));
+ 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;
}
else
{
- throw Parse_error(path_builder.path(), "unknown key");
+ throw Parse_error(entry_value.location, path_builder.path(), "unknown key");
}
}
- auto retval = 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"),
- get_value_or_throw_parse_error(revision, nullptr, "missing revision"),
- get_value_or_throw_parse_error(instructions, nullptr, "missing instructions"),
- get_value_or_throw_parse_error(operand_kinds, nullptr, "missing operand_kinds"));
- throw Parse_error({}, "not finished implementing");
+ return ast::Top_level(
+ get_value_or_throw_parse_error(
+ 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"),
+ get_value_or_throw_parse_error(
+ 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"),
+ 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"),
+ get_value_or_throw_parse_error(
+ std::move(operand_kinds), top_level_value.location, nullptr, "missing operand_kinds"));
}
}
}
#include <vector>
#include "../util/variant.h"
#include "../json/json.h"
+#include "../json/parser.h"
namespace vulkan_cpu
{
}
};
-class Parse_error : public std::runtime_error
+class Parse_error : public json::Parse_error
{
public:
Path path;
- Parse_error(parser::Path path, const std::string &message)
- : runtime_error("at " + path.to_string() + ": " + message), path(std::move(path))
+ Parse_error(json::Location location, parser::Path path, const std::string &message)
+ : json::Parse_error(location, "at " + path.to_string() + ": " + message),
+ path(std::move(path))
{
}
};
#
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
set(sources json.cpp
- parser.cpp)
+ location.cpp
+ parser.cpp
+ source.cpp)
add_library(json STATIC ${sources})
target_link_libraries(json util)
\ No newline at end of file
template <typename Write_Char>
void write_unsigned_integer(Write_Char write_char,
std::uint64_t value,
- unsigned base) noexcept(noexcept(write_char('0')))
+ unsigned base,
+ std::size_t min_length = 1) noexcept(noexcept(write_char('0')))
{
assert(base >= Number_value::min_base && base <= Number_value::max_base);
+ while(min_length > max_integer_buffer_size)
+ {
+ write_char('0');
+ min_length--;
+ }
char buffer[max_integer_buffer_size]{};
std::size_t buffer_used = 0;
- do
+ for(std::size_t i = 0; i < min_length || value != 0; i++)
{
assert(buffer_used < max_integer_buffer_size);
buffer[buffer_used++] = get_digit_char(value % base, false);
value /= base;
- } while(value != 0);
+ }
for(std::size_t i = 0, j = buffer_used - 1; i < buffer_used; i++, j--)
write_char(buffer[j]);
}
std::string Number_value::append_unsigned_integer_to_string(std::uint64_t value,
std::string buffer,
- unsigned base)
+ unsigned base,
+ std::size_t min_length)
{
write_unsigned_integer(
[&](char ch)
buffer += ch;
},
value,
- base);
+ base,
+ min_length);
return buffer;
}
char *output_buffer,
std::size_t output_buffer_size,
bool require_null_terminator,
- unsigned base) noexcept
+ unsigned base,
+ std::size_t min_length) noexcept
{
if(output_buffer_size == 0)
return 0;
output_buffer[used_buffer_size++] = ch;
},
value,
- base);
+ base,
+ min_length);
if(used_buffer_size < output_buffer_size)
output_buffer[used_buffer_size] = '\0'; // add the null terminator if there is space
return used_buffer_size; // report used buffer excluding the null terminator
#include <type_traits>
#include <cassert>
#include "../util/variant.h"
+#include "location.h"
namespace vulkan_cpu
{
}
static std::string append_unsigned_integer_to_string(std::uint64_t value,
std::string buffer,
- unsigned base = default_base);
+ unsigned base = default_base,
+ std::size_t min_length = 1);
static std::string unsigned_integer_to_string(std::uint64_t value,
std::string buffer = {},
- unsigned base = default_base)
+ unsigned base = default_base,
+ std::size_t min_length = 1)
{
buffer.clear();
- return append_unsigned_integer_to_string(value, std::move(buffer), base);
+ return append_unsigned_integer_to_string(value, std::move(buffer), base, min_length);
}
static std::size_t unsigned_integer_to_buffer(std::uint64_t value,
char *output_buffer,
std::size_t output_buffer_size,
bool require_null_terminator = true,
- unsigned base = default_base) noexcept;
+ unsigned base = default_base,
+ std::size_t min_length = 1) noexcept;
static std::string append_signed_integer_to_string(std::int64_t value,
std::string buffer,
unsigned base = default_base);
}
};
-typedef util::
- variant<Null_value, Boolean_value, String_value, Number_value, Composite_value_pointer> Value;
+struct Object;
+struct Array;
+
+struct Value
+{
+ Location location;
+ util::variant<Null_value, Boolean_value, String_value, Number_value, Composite_value_pointer>
+ value;
+ constexpr Value()
+ {
+ }
+ constexpr explicit Value(Location location, Null_value value)
+ : location(std::move(location)), value(std::move(value))
+ {
+ }
+ constexpr explicit Value(Location location, Boolean_value value)
+ : location(std::move(location)), value(std::move(value))
+ {
+ }
+ explicit Value(Location location, String_value value)
+ : location(std::move(location)), value(std::move(value))
+ {
+ }
+ explicit Value(Location location, Number_value value)
+ : location(std::move(location)), value(std::move(value))
+ {
+ }
+ explicit Value(Location location, Composite_value_pointer value)
+ : location(std::move(location)), value(std::move(value))
+ {
+ }
+ explicit Value(Location location, Array &&value);
+ explicit Value(Location location, Object &&value);
+ Value duplicate() const;
+ Null_value &get_null()
+ {
+ return util::get<Null_value>(value);
+ }
+ const Null_value &get_null() const
+ {
+ return util::get<Null_value>(value);
+ }
+ Boolean_value &get_boolean()
+ {
+ return util::get<Boolean_value>(value);
+ }
+ const Boolean_value &get_boolean() const
+ {
+ return util::get<Boolean_value>(value);
+ }
+ String_value &get_string()
+ {
+ return util::get<String_value>(value);
+ }
+ const String_value &get_string() const
+ {
+ return util::get<String_value>(value);
+ }
+ Number_value &get_number()
+ {
+ return util::get<Number_value>(value);
+ }
+ const Number_value &get_number() const
+ {
+ return util::get<Number_value>(value);
+ }
+ Object &get_object();
+ const Object &get_object() const;
+ Array &get_array();
+ const Array &get_array() const;
+ Value_kind get_value_kind() const noexcept;
+};
struct Composite_value
{
virtual ~Composite_value() = default;
virtual void write(std::ostream &os, Write_state &state) const = 0;
virtual Composite_value_pointer duplicate() const = 0;
- operator Value() const
- {
- return duplicate();
- }
virtual Value_kind get_value_kind() const noexcept = 0;
};
-inline Value duplicate(const Value &v)
+inline Value Value::duplicate() const
{
return util::visit(
- [](const auto &v) -> Value
+ [this](const auto &v) -> Value
{
- return v->duplicate();
+ return Value(location, v->duplicate());
},
- v);
+ value);
}
struct Object final : public Composite_value
std::unordered_map<std::string, Value> new_values;
for(auto &entry : values)
{
- new_values.emplace(std::get<0>(entry), ast::duplicate(std::get<1>(entry)));
+ new_values.emplace(std::get<0>(entry), std::get<1>(entry).duplicate());
}
return std::make_shared<Object>(std::move(new_values));
}
}
};
+inline Object &Value::get_object()
+{
+ return dynamic_cast<Object &>(*util::get<Composite_value_pointer>(value));
+}
+
+inline const Object &Value::get_object() const
+{
+ return dynamic_cast<const Object &>(*util::get<Composite_value_pointer>(value));
+}
+
struct Array final : public Composite_value
{
std::vector<Value> values;
std::vector<Value> new_values;
new_values.reserve(values.size());
for(auto &value : values)
- new_values.emplace_back(ast::duplicate(value));
+ new_values.emplace_back(value.duplicate());
return std::make_shared<Array>(std::move(new_values));
}
Value_kind get_value_kind() const noexcept override
}
};
-inline Value_kind get_value_kind(const Value &v) noexcept
+inline Array &Value::get_array()
+{
+ return dynamic_cast<Array &>(*util::get<Composite_value_pointer>(value));
+}
+
+inline const Array &Value::get_array() const
+{
+ return dynamic_cast<const Array &>(*util::get<Composite_value_pointer>(value));
+}
+
+inline Value_kind Value::get_value_kind() const noexcept
{
return util::visit(
- [&](const auto &v) -> Value_kind
+ [](const auto &v) -> Value_kind
{
return v->get_value_kind();
},
- v);
+ value);
+}
+
+inline Value::Value(Location location, Array &&value)
+ : location(std::move(location)), value(std::make_shared<Array>(std::move(value)))
+{
+}
+
+inline Value::Value(Location location, Object &&value)
+ : location(std::move(location)), value(std::make_shared<Object>(std::move(value)))
+{
}
}
{
v->write(os, state);
},
- v);
+ v.value);
}
inline void write(std::ostream &os, const ast::Value &v, Write_options options = {})
--- /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 "location.h"
+
+namespace vulkan_cpu
+{
+namespace json
+{
+std::ostream &operator<<(std::ostream &os, const Location &v)
+{
+ os << v.to_string();
+ return os;
+}
+}
+}
--- /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 JSON_LOCATION_H_
+#define JSON_LOCATION_H_
+
+#include "source.h"
+#include <string>
+#include <iosfwd>
+
+namespace vulkan_cpu
+{
+namespace json
+{
+struct Location
+{
+ const Source *source;
+ std::size_t char_index;
+ constexpr Location() noexcept : source(nullptr), char_index(0)
+ {
+ }
+ constexpr Location(const json::Source *source, std::size_t char_index) noexcept
+ : source(source),
+ char_index(char_index)
+ {
+ }
+ json::Source::Line_and_index get_line_and_start_index() const noexcept
+ {
+ if(!source)
+ return {};
+ return source->get_line_and_start_index(char_index);
+ }
+ json::Source::Line_and_column get_line_and_column(
+ std::size_t tab_size = json::Source::default_tab_size) const noexcept
+ {
+ if(!source)
+ return {};
+ return source->get_line_and_column(char_index, tab_size);
+ }
+ std::string to_string(std::string buffer = {},
+ std::size_t tab_size = json::Source::default_tab_size) const
+ {
+ buffer.clear();
+ return append_to_string(std::move(buffer));
+ }
+ std::string append_to_string(std::string buffer,
+ std::size_t tab_size = json::Source::default_tab_size) const
+ {
+ if(!source || source->file_name.empty())
+ buffer += "<unknown>";
+ else
+ buffer += source->file_name;
+ buffer += ':';
+ buffer = get_line_and_column(tab_size).append_to_string(std::move(buffer));
+ return buffer;
+ }
+ friend std::ostream &operator<<(std::ostream &os, const Location &v);
+};
+}
+}
+
+#endif /* JSON_LOCATION_H_ */
{
namespace json
{
-namespace
-{
-constexpr bool is_new_line(char ch) noexcept
-{
- return ch == '\r' || ch == '\n';
-}
-
-constexpr bool is_new_line_pair(char ch1, char ch2) noexcept
-{
- return ch1 == '\r' && ch2 == '\n';
-}
-
-template <typename Add_Index>
-void find_line_start_indexes_helper(Add_Index &&add_index,
- const char *contents,
- std::size_t contents_size)
-{
- for(std::size_t i = 0; i < contents_size; i++)
- {
- char ch = contents[i];
- if(i + 1 < contents_size)
- {
- char ch2 = contents[i + 1];
- if(is_new_line_pair(ch, ch2))
- {
- add_index(i + 2);
- i++;
- continue;
- }
- }
- if(is_new_line(ch))
- add_index(i + 1);
- }
-}
-}
-
-std::vector<std::size_t> Source::find_line_start_indexes(const char *contents,
- std::size_t contents_size)
-{
- std::size_t retval_size = 0;
- find_line_start_indexes_helper(
- [&](std::size_t)
- {
- retval_size++;
- },
- contents,
- contents_size);
- std::vector<std::size_t> retval;
- retval.reserve(retval_size);
- find_line_start_indexes_helper(
- [&](std::size_t index)
- {
- retval.push_back(index);
- },
- contents,
- contents_size);
- return retval;
-}
-
-Source Source::load_file(std::string file_name)
-{
- // TODO: add code to use mmap
- std::ifstream is;
- is.exceptions(std::ios::badbit | std::ios::failbit);
- is.open(file_name);
- std::vector<char> buffer;
- while(is.peek() != std::char_traits<char>::eof())
- {
- if(buffer.size() == buffer.capacity())
- buffer.reserve(buffer.size() * 2);
- buffer.push_back(is.get());
- }
- is.close();
- buffer.shrink_to_fit();
- std::size_t contents_size = buffer.size();
- auto buffer_ptr = std::make_shared<std::vector<char>>(std::move(buffer));
- std::shared_ptr<const char> contents(buffer_ptr, buffer_ptr->data());
- return Source(std::move(file_name), std::move(contents), contents_size);
-}
-
-Source Source::load_stdin()
-{
- auto &is = std::cin;
- is.clear();
- auto previous_exceptions = is.exceptions();
- std::vector<char> buffer;
- try
- {
- is.exceptions(std::ios::badbit | std::ios::failbit);
- while(is.peek() != std::char_traits<char>::eof())
- {
- if(buffer.size() == buffer.capacity())
- buffer.reserve(buffer.size() * 2);
- buffer.push_back(is.get());
- }
- }
- catch(...)
- {
- is.clear();
- is.exceptions(previous_exceptions);
- }
- is.clear();
- is.exceptions(previous_exceptions);
- buffer.shrink_to_fit();
- std::size_t contents_size = buffer.size();
- auto buffer_ptr = std::make_shared<std::vector<char>>(std::move(buffer));
- std::shared_ptr<const char> contents(buffer_ptr, buffer_ptr->data());
- return Source("stdin", std::move(contents), contents_size);
-}
-
-std::ostream &operator<<(std::ostream &os, const Source::Line_and_column &v)
-{
- os << v.to_string();
- return os;
-}
-
-Source::Line_and_index Source::get_line_and_start_index(std::size_t char_index) const noexcept
-{
- std::size_t line =
- 1 + line_start_indexes.size()
- + (line_start_indexes.rbegin() - std::lower_bound(line_start_indexes.rbegin(),
- line_start_indexes.rend(),
- char_index,
- std::greater<std::size_t>()));
- return Line_and_index(line, line <= 1 ? 0 : line_start_indexes[line - 2]);
-}
-
-namespace
-{
-constexpr std::size_t get_column_after_tab(std::size_t column, std::size_t tab_size) noexcept
-{
- return tab_size == 0 || column == 0 ? column + 1 :
- column + (tab_size - (column - 1) % tab_size);
-}
-}
-
-Source::Line_and_column Source::get_line_and_column(std::size_t char_index,
- std::size_t tab_size) const noexcept
-{
- auto line_and_start_index = get_line_and_start_index(char_index);
- std::size_t column = 1;
- for(std::size_t i = line_and_start_index.index; i < char_index; i++)
- {
- int ch = contents.get()[i];
- if(ch == '\t')
- column = get_column_after_tab(column, tab_size);
- else
- column++;
- }
- return Line_and_column(line_and_start_index.line, column);
-}
-
-std::ostream &operator<<(std::ostream &os, const Location &v)
-{
- os << v.to_string();
- return os;
-}
-
namespace
{
enum class Token_type
while(is_whitespace(peekc()))
getc();
token_location = Location(source, input_char_index);
- token_value = nullptr;
+ token_value = ast::Value(token_location, nullptr);
bool got_minus = false, got_plus = false;
if(peekc() == '-')
{
{
if(match_buffer_with_string(name, name_size, "null"))
{
- token_value = nullptr;
+ token_value = ast::Value(token_location, nullptr);
token_type = json::Token_type::null_literal;
return;
}
if(match_buffer_with_string(name, name_size, "false"))
{
- token_value = false;
+ token_value = ast::Value(token_location, false);
token_type = json::Token_type::false_literal;
return;
}
if(match_buffer_with_string(name, name_size, "true"))
{
- token_value = true;
+ token_value = ast::Value(token_location, true);
token_type = json::Token_type::true_literal;
return;
}
|| match_buffer_with_string(name, name_size, "nan")
|| match_buffer_with_string(name, name_size, "NAN"))
{
- token_value = std::numeric_limits<double>::quiet_NaN();
+ token_value =
+ ast::Value(token_location, std::numeric_limits<double>::quiet_NaN());
token_type = json::Token_type::number;
return;
}
|| match_buffer_with_string(name, name_size, "inf")
|| match_buffer_with_string(name, name_size, "INF"))
{
- token_value = got_minus ? -std::numeric_limits<double>::infinity() :
- std::numeric_limits<double>::infinity();
+ token_value = ast::Value(token_location,
+ got_minus ? -std::numeric_limits<double>::infinity() :
+ std::numeric_limits<double>::infinity());
token_type = json::Token_type::number;
return;
}
mantissa
* pow(util::soft_float::ExtendedFloat(static_cast<std::uint64_t>(10)), exponent);
token_type = json::Token_type::number;
- token_value = static_cast<double>(value);
+ token_value = ast::Value(token_location, static_cast<double>(value));
return;
}
if(peekc() == '\"' || (options.allow_single_quote_strings && peekc() == '\''))
}
}
token_type = json::Token_type::string;
- token_value = std::move(value);
+ token_value = ast::Value(token_location, std::move(value));
return;
}
switch(peekc())
{
case eof:
token_type = json::Token_type::eof;
- token_value = nullptr;
+ token_value = ast::Value(token_location, nullptr);
return;
case '[':
getc();
token_type = json::Token_type::l_bracket;
- token_value = nullptr;
+ token_value = ast::Value(token_location, nullptr);
return;
case ']':
getc();
token_type = json::Token_type::r_bracket;
- token_value = nullptr;
+ token_value = ast::Value(token_location, nullptr);
return;
case '{':
getc();
token_type = json::Token_type::l_brace;
- token_value = nullptr;
+ token_value = ast::Value(token_location, nullptr);
return;
case '}':
getc();
token_type = json::Token_type::r_brace;
- token_value = nullptr;
+ token_value = ast::Value(token_location, nullptr);
return;
case ':':
getc();
token_type = json::Token_type::colon;
- token_value = nullptr;
+ token_value = ast::Value(token_location, nullptr);
return;
case ',':
getc();
token_type = json::Token_type::comma;
- token_value = nullptr;
+ token_value = ast::Value(token_location, nullptr);
return;
}
throw Parse_error(token_location, "invalid character");
case Token_type::l_bracket:
{
std::vector<ast::Value> values;
+ auto array_location = tokenizer.token_location;
tokenizer.next();
if(tokenizer.token_type == Token_type::r_bracket)
{
throw Parse_error(tokenizer.token_location, "missing , or ]");
}
}
- return ast::Array(std::move(values));
+ return ast::Value(array_location, ast::Array(std::move(values)));
}
case Token_type::l_brace:
{
std::unordered_map<std::string, ast::Value> values;
+ auto object_location = tokenizer.token_location;
tokenizer.next();
if(tokenizer.token_type == Token_type::r_brace)
{
{
if(tokenizer.token_type != Token_type::string)
throw Parse_error(tokenizer.token_location, "missing string");
- auto string_value = std::move(util::get<ast::String_value>(tokenizer.get()).value);
+ auto string_value = std::move(tokenizer.get().get_string().value);
if(tokenizer.token_type != Token_type::colon)
throw Parse_error(tokenizer.token_location, "missing ':'");
tokenizer.next();
throw Parse_error(tokenizer.token_location, "missing ',' or '}'");
}
}
- return ast::Object(std::move(values));
+ return ast::Value(object_location, ast::Object(std::move(values)));
}
default:
break;
#include <vector>
#include <iosfwd>
#include "json.h"
+#include "source.h"
+#include "location.h"
#include "../util/optional.h"
namespace vulkan_cpu
{
namespace json
{
-struct Source
-{
- std::string file_name;
- std::shared_ptr<const char> contents; // use a shared_ptr so you can use mmap-ed memory
- std::size_t contents_size;
- /** doesn't have first line to save memory */
- std::vector<std::size_t> line_start_indexes;
- static std::vector<std::size_t> find_line_start_indexes(const char *contents,
- std::size_t contents_size);
- Source(Source &&) = default;
- Source(const Source &) = delete;
- Source &operator=(Source &&) = default;
- Source &operator=(const Source &) = delete;
- Source() : file_name(), contents(), contents_size(0), line_start_indexes()
- {
- }
- explicit Source(std::string file_name) noexcept : file_name(std::move(file_name)),
- contents(),
- contents_size(0),
- line_start_indexes()
- {
- }
- Source(std::string file_name,
- std::shared_ptr<const char> contents,
- std::size_t contents_size) noexcept
- : file_name(std::move(file_name)),
- contents(std::move(contents)),
- contents_size(contents_size),
- line_start_indexes(find_line_start_indexes(this->contents.get(), contents_size))
- {
- }
- Source(std::string file_name, std::string contents_in)
- : file_name(file_name),
- contents(),
- contents_size(contents_in.size()),
- line_start_indexes(find_line_start_indexes(contents_in.data(), contents_size))
- {
- auto str = std::make_shared<std::string>(std::move(contents_in));
- contents = std::shared_ptr<const char>(str, str->data());
- }
- Source(std::string file_name, std::vector<char> contents_in)
- : file_name(file_name),
- contents(),
- contents_size(contents_in.size()),
- line_start_indexes(find_line_start_indexes(contents_in.data(), contents_size))
- {
- auto str = std::make_shared<std::vector<char>>(std::move(contents_in));
- contents = std::shared_ptr<const char>(str, str->data());
- }
- Source(std::string file_name, std::vector<unsigned char> contents_in)
- : file_name(file_name),
- contents(),
- contents_size(contents_in.size()),
- line_start_indexes(find_line_start_indexes(
- reinterpret_cast<const char *>(contents_in.data()), contents_size))
- {
- auto str = std::make_shared<std::vector<unsigned char>>(std::move(contents_in));
- contents = std::shared_ptr<const char>(str, reinterpret_cast<const char *>(str->data()));
- }
- explicit operator bool() const noexcept
- {
- return contents != nullptr;
- }
- static Source load_file(std::string file_name);
- static Source load_stdin();
- struct Line_and_index
- {
- std::size_t line;
- std::size_t index;
- constexpr Line_and_index() noexcept : line(), index()
- {
- }
- constexpr Line_and_index(std::size_t line, std::size_t index) noexcept : line(line),
- index(index)
- {
- }
- };
- struct Line_and_column
- {
- std::size_t line;
- std::size_t column;
- constexpr Line_and_column() noexcept : line(), column()
- {
- }
- constexpr Line_and_column(std::size_t line, std::size_t column) noexcept : line(line),
- column(column)
- {
- }
- std::string append_to_string(std::string buffer) const
- {
- buffer = ast::Number_value::append_unsigned_integer_to_string(line, std::move(buffer));
- buffer += ':';
- buffer =
- ast::Number_value::append_unsigned_integer_to_string(column, std::move(buffer));
- return buffer;
- }
- std::string to_string(std::string buffer = {}) const
- {
- buffer.clear();
- return append_to_string(std::move(buffer));
- }
- friend std::ostream &operator<<(std::ostream &os, const Line_and_column &v);
- };
- static constexpr std::size_t default_tab_size = 8;
- Line_and_index get_line_and_start_index(std::size_t char_index) const noexcept;
- Line_and_column get_line_and_column(std::size_t char_index,
- std::size_t tab_size = default_tab_size) const noexcept;
-};
-
-struct Location
-{
- const Source *source;
- std::size_t char_index;
- constexpr Location() noexcept : source(nullptr), char_index(0)
- {
- }
- constexpr Location(const json::Source *source, std::size_t char_index) noexcept
- : source(source),
- char_index(char_index)
- {
- }
- json::Source::Line_and_index get_line_and_start_index() const noexcept
- {
- if(!source)
- return {};
- return source->get_line_and_start_index(char_index);
- }
- json::Source::Line_and_column get_line_and_column(
- std::size_t tab_size = json::Source::default_tab_size) const noexcept
- {
- if(!source)
- return {};
- return source->get_line_and_column(char_index, tab_size);
- }
- std::string to_string(std::string buffer = {},
- std::size_t tab_size = json::Source::default_tab_size) const
- {
- buffer.clear();
- return append_to_string(std::move(buffer));
- }
- std::string append_to_string(std::string buffer,
- std::size_t tab_size = json::Source::default_tab_size) const
- {
- if(!source || source->file_name.empty())
- buffer += "<unknown>";
- else
- buffer += source->file_name;
- buffer += ':';
- buffer = get_line_and_column(tab_size).append_to_string(std::move(buffer));
- return buffer;
- }
- friend std::ostream &operator<<(std::ostream &os, const Location &v);
-};
-
class Parse_error : public std::runtime_error
{
public:
Location location;
Parse_error(json::Location location, const std::string &message)
- : runtime_error(location.to_string() + ": " + message)
+ : runtime_error(location.to_string() + ": error: " + message)
{
}
Parse_error(json::Location location, const char *message)
- : runtime_error(location.to_string() + ": " + message)
+ : runtime_error(location.to_string() + ": error: " + message)
{
}
};
--- /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 "source.h"
+#include "json.h"
+#include <iostream>
+#include <fstream>
+#include <algorithm>
+
+namespace vulkan_cpu
+{
+namespace json
+{
+std::string Source::Line_and_column::append_to_string(std::string buffer) const
+{
+ buffer = ast::Number_value::append_unsigned_integer_to_string(line, std::move(buffer));
+ buffer += ':';
+ buffer = ast::Number_value::append_unsigned_integer_to_string(column, std::move(buffer));
+ return buffer;
+}
+
+namespace
+{
+constexpr bool is_new_line(char ch) noexcept
+{
+ return ch == '\r' || ch == '\n';
+}
+
+constexpr bool is_new_line_pair(char ch1, char ch2) noexcept
+{
+ return ch1 == '\r' && ch2 == '\n';
+}
+
+template <typename Add_Index>
+void find_line_start_indexes_helper(Add_Index &&add_index,
+ const char *contents,
+ std::size_t contents_size)
+{
+ for(std::size_t i = 0; i < contents_size; i++)
+ {
+ char ch = contents[i];
+ if(i + 1 < contents_size)
+ {
+ char ch2 = contents[i + 1];
+ if(is_new_line_pair(ch, ch2))
+ {
+ add_index(i + 2);
+ i++;
+ continue;
+ }
+ }
+ if(is_new_line(ch))
+ add_index(i + 1);
+ }
+}
+}
+
+std::vector<std::size_t> Source::find_line_start_indexes(const char *contents,
+ std::size_t contents_size)
+{
+ std::size_t retval_size = 0;
+ find_line_start_indexes_helper(
+ [&](std::size_t)
+ {
+ retval_size++;
+ },
+ contents,
+ contents_size);
+ std::vector<std::size_t> retval;
+ retval.reserve(retval_size);
+ find_line_start_indexes_helper(
+ [&](std::size_t index)
+ {
+ retval.push_back(index);
+ },
+ contents,
+ contents_size);
+ return retval;
+}
+
+Source Source::load_file(std::string file_name)
+{
+ // TODO: add code to use mmap
+ std::ifstream is;
+ is.exceptions(std::ios::badbit | std::ios::failbit);
+ is.open(file_name);
+ std::vector<char> buffer;
+ while(is.peek() != std::char_traits<char>::eof())
+ {
+ if(buffer.size() == buffer.capacity())
+ buffer.reserve(buffer.size() * 2);
+ buffer.push_back(is.get());
+ }
+ is.close();
+ buffer.shrink_to_fit();
+ std::size_t contents_size = buffer.size();
+ auto buffer_ptr = std::make_shared<std::vector<char>>(std::move(buffer));
+ std::shared_ptr<const char> contents(buffer_ptr, buffer_ptr->data());
+ return Source(std::move(file_name), std::move(contents), contents_size);
+}
+
+Source Source::load_stdin()
+{
+ auto &is = std::cin;
+ is.clear();
+ auto previous_exceptions = is.exceptions();
+ std::vector<char> buffer;
+ try
+ {
+ is.exceptions(std::ios::badbit | std::ios::failbit);
+ while(is.peek() != std::char_traits<char>::eof())
+ {
+ if(buffer.size() == buffer.capacity())
+ buffer.reserve(buffer.size() * 2);
+ buffer.push_back(is.get());
+ }
+ }
+ catch(...)
+ {
+ is.clear();
+ is.exceptions(previous_exceptions);
+ }
+ is.clear();
+ is.exceptions(previous_exceptions);
+ buffer.shrink_to_fit();
+ std::size_t contents_size = buffer.size();
+ auto buffer_ptr = std::make_shared<std::vector<char>>(std::move(buffer));
+ std::shared_ptr<const char> contents(buffer_ptr, buffer_ptr->data());
+ return Source("stdin", std::move(contents), contents_size);
+}
+
+std::ostream &operator<<(std::ostream &os, const Source::Line_and_column &v)
+{
+ os << v.to_string();
+ return os;
+}
+
+Source::Line_and_index Source::get_line_and_start_index(std::size_t char_index) const noexcept
+{
+ std::size_t line =
+ 1 + line_start_indexes.size()
+ + (line_start_indexes.rbegin() - std::lower_bound(line_start_indexes.rbegin(),
+ line_start_indexes.rend(),
+ char_index,
+ std::greater<std::size_t>()));
+ return Line_and_index(line, line <= 1 ? 0 : line_start_indexes[line - 2]);
+}
+
+namespace
+{
+constexpr std::size_t get_column_after_tab(std::size_t column, std::size_t tab_size) noexcept
+{
+ return tab_size == 0 || column == 0 ? column + 1 :
+ column + (tab_size - (column - 1) % tab_size);
+}
+}
+
+Source::Line_and_column Source::get_line_and_column(std::size_t char_index,
+ std::size_t tab_size) const noexcept
+{
+ auto line_and_start_index = get_line_and_start_index(char_index);
+ std::size_t column = 1;
+ for(std::size_t i = line_and_start_index.index; i < char_index; i++)
+ {
+ int ch = contents.get()[i];
+ if(ch == '\t')
+ column = get_column_after_tab(column, tab_size);
+ else
+ column++;
+ }
+ return Line_and_column(line_and_start_index.line, column);
+}
+}
+}
\ 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 JSON_SOURCE_H_
+#define JSON_SOURCE_H_
+
+#include <string>
+#include <memory>
+#include <vector>
+#include <iosfwd>
+
+namespace vulkan_cpu
+{
+namespace json
+{
+struct Source
+{
+ std::string file_name;
+ std::shared_ptr<const char> contents; // use a shared_ptr so you can use mmap-ed memory
+ std::size_t contents_size;
+ /** doesn't have first line to save memory */
+ std::vector<std::size_t> line_start_indexes;
+ static std::vector<std::size_t> find_line_start_indexes(const char *contents,
+ std::size_t contents_size);
+ Source(Source &&) = default;
+ Source(const Source &) = delete;
+ Source &operator=(Source &&) = default;
+ Source &operator=(const Source &) = delete;
+ Source() : file_name(), contents(), contents_size(0), line_start_indexes()
+ {
+ }
+ explicit Source(std::string file_name) noexcept : file_name(std::move(file_name)),
+ contents(),
+ contents_size(0),
+ line_start_indexes()
+ {
+ }
+ Source(std::string file_name,
+ std::shared_ptr<const char> contents,
+ std::size_t contents_size) noexcept
+ : file_name(std::move(file_name)),
+ contents(std::move(contents)),
+ contents_size(contents_size),
+ line_start_indexes(find_line_start_indexes(this->contents.get(), contents_size))
+ {
+ }
+ Source(std::string file_name, std::string contents_in)
+ : file_name(file_name),
+ contents(),
+ contents_size(contents_in.size()),
+ line_start_indexes(find_line_start_indexes(contents_in.data(), contents_size))
+ {
+ auto str = std::make_shared<std::string>(std::move(contents_in));
+ contents = std::shared_ptr<const char>(str, str->data());
+ }
+ Source(std::string file_name, std::vector<char> contents_in)
+ : file_name(file_name),
+ contents(),
+ contents_size(contents_in.size()),
+ line_start_indexes(find_line_start_indexes(contents_in.data(), contents_size))
+ {
+ auto str = std::make_shared<std::vector<char>>(std::move(contents_in));
+ contents = std::shared_ptr<const char>(str, str->data());
+ }
+ Source(std::string file_name, std::vector<unsigned char> contents_in)
+ : file_name(file_name),
+ contents(),
+ contents_size(contents_in.size()),
+ line_start_indexes(find_line_start_indexes(
+ reinterpret_cast<const char *>(contents_in.data()), contents_size))
+ {
+ auto str = std::make_shared<std::vector<unsigned char>>(std::move(contents_in));
+ contents = std::shared_ptr<const char>(str, reinterpret_cast<const char *>(str->data()));
+ }
+ explicit operator bool() const noexcept
+ {
+ return contents != nullptr;
+ }
+ static Source load_file(std::string file_name);
+ static Source load_stdin();
+ struct Line_and_index
+ {
+ std::size_t line;
+ std::size_t index;
+ constexpr Line_and_index() noexcept : line(), index()
+ {
+ }
+ constexpr Line_and_index(std::size_t line, std::size_t index) noexcept : line(line),
+ index(index)
+ {
+ }
+ };
+ struct Line_and_column
+ {
+ std::size_t line;
+ std::size_t column;
+ constexpr Line_and_column() noexcept : line(), column()
+ {
+ }
+ constexpr Line_and_column(std::size_t line, std::size_t column) noexcept : line(line),
+ column(column)
+ {
+ }
+ std::string append_to_string(std::string buffer) const;
+ std::string to_string(std::string buffer = {}) const
+ {
+ buffer.clear();
+ return append_to_string(std::move(buffer));
+ }
+ friend std::ostream &operator<<(std::ostream &os, const Line_and_column &v);
+ };
+ static constexpr std::size_t default_tab_size = 8;
+ Line_and_index get_line_and_start_index(std::size_t char_index) const noexcept;
+ Line_and_column get_line_and_column(std::size_t char_index,
+ std::size_t tab_size = default_tab_size) const noexcept;
+};
+}
+}
+
+#endif /* JSON_SOURCE_H_ */