From: Jacob Lifshay Date: Mon, 5 Jun 2017 05:35:15 +0000 (-0700) Subject: working on implementing generate_spirv_parser X-Git-Tag: gsoc-2017~108 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=6ffb63fe72ca4aa29d46f7c33fbddd08ef65cb84;p=kazan.git working on implementing generate_spirv_parser --- diff --git a/src/generate_spirv_parser/ast.cpp b/src/generate_spirv_parser/ast.cpp index 9e4b259..e80b9ca 100644 --- a/src/generate_spirv_parser/ast.cpp +++ b/src/generate_spirv_parser/ast.cpp @@ -21,3 +21,114 @@ * */ #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)); +} +} +} +} diff --git a/src/generate_spirv_parser/ast.h b/src/generate_spirv_parser/ast.h index 5d59a95..6c9d044 100644 --- a/src/generate_spirv_parser/ast.h +++ b/src/generate_spirv_parser/ast.h @@ -26,6 +26,8 @@ #include "../json/json.h" #include +#include +#include namespace vulkan_cpu { @@ -35,23 +37,136 @@ namespace ast { struct Copyright { - json::ast::Array value; - Copyright() : value() + std::vector lines; + Copyright() : lines() { } - explicit Copyright(json::ast::Array value) noexcept : value(std::move(value)) + explicit Copyright(std::vector 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 enumerants; + explicit Enumerants(std::vector 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 values; + json::ast::Value to_json() const; + }; + typedef util::variant 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(value); + case Category::id: + case Category::literal: + return util::holds_alternative(value); + case Category::composite: + return util::holds_alternative(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_kinds; + explicit Operand_kinds(std::vector operand_kinds) noexcept + : operand_kinds(std::move(operand_kinds)) + { + } + json::ast::Value to_json() const; }; struct Top_level @@ -79,6 +194,7 @@ struct Top_level operand_kinds(std::move(operand_kinds)) { } + json::ast::Value to_json() const; }; } } diff --git a/src/generate_spirv_parser/generate_spirv_parser.cpp b/src/generate_spirv_parser/generate_spirv_parser.cpp index eb48aee..3bfb0eb 100644 --- a/src/generate_spirv_parser/generate_spirv_parser.cpp +++ b/src/generate_spirv_parser/generate_spirv_parser.cpp @@ -25,6 +25,7 @@ #include "../json/parser.h" #include "parser.h" #include "../util/optional.h" +#include namespace vulkan_cpu { @@ -47,15 +48,20 @@ int generate_spirv_parser_main(int argc, char **argv) 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; } } diff --git a/src/generate_spirv_parser/parser.cpp b/src/generate_spirv_parser/parser.cpp index ddaa34a..7544b1b 100644 --- a/src/generate_spirv_parser/parser.cpp +++ b/src/generate_spirv_parser/parser.cpp @@ -53,72 +53,64 @@ std::string Path::to_string() const namespace { -template +template Value get_value_or_throw_parse_error(util::optional 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 +Value get_value_or_throw_parse_error(util::optional 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(*util::get(value)); - for(std::size_t index = 0; index < copyright_array.values.size(); index++) - { - Path_builder 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 +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(*util::get(value)); - static_cast(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 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(*util::get(value)); - static_cast(instructions_array); -#warning finish - return ast::Instructions(); -} template 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(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') @@ -134,6 +126,22 @@ constexpr int get_digit_value(int ch, unsigned base) noexcept 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 T parse_hex_integer_string(const json::ast::Value &value, const Path_builder_base *parent_path_builder, @@ -141,13 +149,15 @@ T parse_hex_integer_string(const json::ast::Value &value, 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(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::max(); constexpr unsigned base = 0x10; @@ -159,30 +169,277 @@ T parse_hex_integer_string(const json::ast::Value &value, 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(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 +struct Enum_value_descriptor +{ + const char *name; + Enum value; + constexpr Enum_value_descriptor(const char *name, Enum value) noexcept : name(name), + value(value) + { + } +}; + +template +constexpr std::initializer_list> make_enum_value_descriptors = { + {Get_Name(Values), Values}...}; + +template +Enum parse_enum_string(const json::ast::Value &value, + const Path_builder_base *parent_path_builder, + const char *name, + std::initializer_list> 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 lines; + lines.reserve(copyright_array.values.size()); + for(std::size_t index = 0; index < copyright_array.values.size(); index++) + { + Path_builder 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(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 enumerants; + enumerants.reserve(enumerants_array.values.size()); + for(std::size_t index = 0; index < enumerants_array.values.size(); index++) + { + Path_builder 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( + std::move(entry_value), + path_builder, + "category", + make_enum_value_descriptors); + }); + 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 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 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 bases; + bases.reserve(bases_array.values.size()); + for(std::size_t i = 0; i < bases_array.values.size(); i++) + { + Path_builder 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 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 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(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( - *util::get(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 copyright; util::optional magic_number; util::optional major_version; @@ -226,18 +483,24 @@ ast::Top_level parse(json::ast::Value &&top_level_value) } 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")); } } } diff --git a/src/generate_spirv_parser/parser.h b/src/generate_spirv_parser/parser.h index 9ef3035..d4f91f2 100644 --- a/src/generate_spirv_parser/parser.h +++ b/src/generate_spirv_parser/parser.h @@ -31,6 +31,7 @@ #include #include "../util/variant.h" #include "../json/json.h" +#include "../json/parser.h" namespace vulkan_cpu { @@ -98,12 +99,13 @@ struct Path_builder final : public Path_builder_base } }; -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)) { } }; diff --git a/src/json/CMakeLists.txt b/src/json/CMakeLists.txt index 4fd34f8..c142b58 100644 --- a/src/json/CMakeLists.txt +++ b/src/json/CMakeLists.txt @@ -20,6 +20,8 @@ # 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 diff --git a/src/json/json.cpp b/src/json/json.cpp index ad1582d..2654676 100644 --- a/src/json/json.cpp +++ b/src/json/json.cpp @@ -168,17 +168,23 @@ constexpr std::size_t max_integer_buffer_size = 64; // max number of digits is b template 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]); } @@ -383,7 +389,8 @@ std::size_t Number_value::double_to_buffer(double value, 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) @@ -391,7 +398,8 @@ std::string Number_value::append_unsigned_integer_to_string(std::uint64_t value, buffer += ch; }, value, - base); + base, + min_length); return buffer; } @@ -399,7 +407,8 @@ std::size_t Number_value::unsigned_integer_to_buffer(std::uint64_t value, 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; @@ -414,7 +423,8 @@ std::size_t Number_value::unsigned_integer_to_buffer(std::uint64_t value, 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 diff --git a/src/json/json.h b/src/json/json.h index d32acdc..9350e18 100644 --- a/src/json/json.h +++ b/src/json/json.h @@ -34,6 +34,7 @@ #include #include #include "../util/variant.h" +#include "location.h" namespace vulkan_cpu { @@ -238,19 +239,22 @@ struct Number_value final } 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); @@ -334,8 +338,78 @@ public: } }; -typedef util:: - variant Value; +struct Object; +struct Array; + +struct Value +{ + Location location; + util::variant + 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(value); + } + const Null_value &get_null() const + { + return util::get(value); + } + Boolean_value &get_boolean() + { + return util::get(value); + } + const Boolean_value &get_boolean() const + { + return util::get(value); + } + String_value &get_string() + { + return util::get(value); + } + const String_value &get_string() const + { + return util::get(value); + } + Number_value &get_number() + { + return util::get(value); + } + const Number_value &get_number() const + { + return util::get(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 { @@ -343,21 +417,17 @@ 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 @@ -375,7 +445,7 @@ struct Object final : public Composite_value std::unordered_map 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(std::move(new_values)); } @@ -385,6 +455,16 @@ struct Object final : public Composite_value } }; +inline Object &Value::get_object() +{ + return dynamic_cast(*util::get(value)); +} + +inline const Object &Value::get_object() const +{ + return dynamic_cast(*util::get(value)); +} + struct Array final : public Composite_value { std::vector values; @@ -400,7 +480,7 @@ struct Array final : public Composite_value std::vector 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(std::move(new_values)); } Value_kind get_value_kind() const noexcept override @@ -409,14 +489,34 @@ struct Array final : public Composite_value } }; -inline Value_kind get_value_kind(const Value &v) noexcept +inline Array &Value::get_array() +{ + return dynamic_cast(*util::get(value)); +} + +inline const Array &Value::get_array() const +{ + return dynamic_cast(*util::get(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(std::move(value))) +{ +} + +inline Value::Value(Location location, Object &&value) + : location(std::move(location)), value(std::make_shared(std::move(value))) +{ } } @@ -427,7 +527,7 @@ inline void write(std::ostream &os, const ast::Value &v, Write_state &state) { v->write(os, state); }, - v); + v.value); } inline void write(std::ostream &os, const ast::Value &v, Write_options options = {}) diff --git a/src/json/location.cpp b/src/json/location.cpp new file mode 100644 index 0000000..7220314 --- /dev/null +++ b/src/json/location.cpp @@ -0,0 +1,35 @@ +/* +* 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; +} +} +} diff --git a/src/json/location.h b/src/json/location.h new file mode 100644 index 0000000..2ffecb7 --- /dev/null +++ b/src/json/location.h @@ -0,0 +1,82 @@ +/* + * 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 +#include + +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 += ""; + 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_ */ diff --git a/src/json/parser.cpp b/src/json/parser.cpp index 36ed166..e884a93 100644 --- a/src/json/parser.cpp +++ b/src/json/parser.cpp @@ -32,164 +32,6 @@ namespace vulkan_cpu { 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 -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 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 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 buffer; - while(is.peek() != std::char_traits::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::move(buffer)); - std::shared_ptr 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 buffer; - try - { - is.exceptions(std::ios::badbit | std::ios::failbit); - while(is.peek() != std::char_traits::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::move(buffer)); - std::shared_ptr 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())); - 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 @@ -335,7 +177,7 @@ public: 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() == '-') { @@ -360,19 +202,19 @@ public: { 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; } @@ -383,7 +225,8 @@ public: || match_buffer_with_string(name, name_size, "nan") || match_buffer_with_string(name, name_size, "NAN")) { - token_value = std::numeric_limits::quiet_NaN(); + token_value = + ast::Value(token_location, std::numeric_limits::quiet_NaN()); token_type = json::Token_type::number; return; } @@ -393,8 +236,9 @@ public: || match_buffer_with_string(name, name_size, "inf") || match_buffer_with_string(name, name_size, "INF")) { - token_value = got_minus ? -std::numeric_limits::infinity() : - std::numeric_limits::infinity(); + token_value = ast::Value(token_location, + got_minus ? -std::numeric_limits::infinity() : + std::numeric_limits::infinity()); token_type = json::Token_type::number; return; } @@ -473,7 +317,7 @@ public: mantissa * pow(util::soft_float::ExtendedFloat(static_cast(10)), exponent); token_type = json::Token_type::number; - token_value = static_cast(value); + token_value = ast::Value(token_location, static_cast(value)); return; } if(peekc() == '\"' || (options.allow_single_quote_strings && peekc() == '\'')) @@ -565,44 +409,44 @@ public: } } 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"); @@ -624,6 +468,7 @@ ast::Value parse_value(Tokenizer &tokenizer) case Token_type::l_bracket: { std::vector values; + auto array_location = tokenizer.token_location; tokenizer.next(); if(tokenizer.token_type == Token_type::r_bracket) { @@ -647,11 +492,12 @@ ast::Value parse_value(Tokenizer &tokenizer) 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 values; + auto object_location = tokenizer.token_location; tokenizer.next(); if(tokenizer.token_type == Token_type::r_brace) { @@ -663,7 +509,7 @@ ast::Value parse_value(Tokenizer &tokenizer) { if(tokenizer.token_type != Token_type::string) throw Parse_error(tokenizer.token_location, "missing string"); - auto string_value = std::move(util::get(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(); @@ -681,7 +527,7 @@ ast::Value parse_value(Tokenizer &tokenizer) 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; diff --git a/src/json/parser.h b/src/json/parser.h index ddc3ae5..dc14771 100644 --- a/src/json/parser.h +++ b/src/json/parser.h @@ -30,176 +30,24 @@ #include #include #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 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 line_start_indexes; - static std::vector 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 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::move(contents_in)); - contents = std::shared_ptr(str, str->data()); - } - Source(std::string file_name, std::vector 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::move(contents_in)); - contents = std::shared_ptr(str, str->data()); - } - Source(std::string file_name, std::vector contents_in) - : file_name(file_name), - contents(), - contents_size(contents_in.size()), - line_start_indexes(find_line_start_indexes( - reinterpret_cast(contents_in.data()), contents_size)) - { - auto str = std::make_shared>(std::move(contents_in)); - contents = std::shared_ptr(str, reinterpret_cast(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 += ""; - 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) { } }; diff --git a/src/json/source.cpp b/src/json/source.cpp new file mode 100644 index 0000000..dc0d7ad --- /dev/null +++ b/src/json/source.cpp @@ -0,0 +1,194 @@ +/* +* 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 +#include +#include + +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 +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 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 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 buffer; + while(is.peek() != std::char_traits::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::move(buffer)); + std::shared_ptr 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 buffer; + try + { + is.exceptions(std::ios::badbit | std::ios::failbit); + while(is.peek() != std::char_traits::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::move(buffer)); + std::shared_ptr 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())); + 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 diff --git a/src/json/source.h b/src/json/source.h new file mode 100644 index 0000000..a72184f --- /dev/null +++ b/src/json/source.h @@ -0,0 +1,140 @@ +/* + * 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 +#include +#include +#include + +namespace vulkan_cpu +{ +namespace json +{ +struct Source +{ + std::string file_name; + std::shared_ptr 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 line_start_indexes; + static std::vector 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 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::move(contents_in)); + contents = std::shared_ptr(str, str->data()); + } + Source(std::string file_name, std::vector 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::move(contents_in)); + contents = std::shared_ptr(str, str->data()); + } + Source(std::string file_name, std::vector contents_in) + : file_name(file_name), + contents(), + contents_size(contents_in.size()), + line_start_indexes(find_line_start_indexes( + reinterpret_cast(contents_in.data()), contents_size)) + { + auto str = std::make_shared>(std::move(contents_in)); + contents = std::shared_ptr(str, reinterpret_cast(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_ */