working on implementing generate_spirv_parser
authorJacob Lifshay <programmerjake@gmail.com>
Mon, 5 Jun 2017 05:35:15 +0000 (22:35 -0700)
committerJacob Lifshay <programmerjake@gmail.com>
Mon, 5 Jun 2017 05:35:15 +0000 (22:35 -0700)
14 files changed:
src/generate_spirv_parser/ast.cpp
src/generate_spirv_parser/ast.h
src/generate_spirv_parser/generate_spirv_parser.cpp
src/generate_spirv_parser/parser.cpp
src/generate_spirv_parser/parser.h
src/json/CMakeLists.txt
src/json/json.cpp
src/json/json.h
src/json/location.cpp [new file with mode: 0644]
src/json/location.h [new file with mode: 0644]
src/json/parser.cpp
src/json/parser.h
src/json/source.cpp [new file with mode: 0644]
src/json/source.h [new file with mode: 0644]

index 9e4b259ce148044151ad191c6ade518cadf255be..e80b9cacaa72ab866ef78d2c6a37616487f4611c 100644 (file)
 *
 */
 #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));
+}
+}
+}
+}
index 5d59a95d2b63c0060fd0f3210e843fac21bc6d59..6c9d04483d3c774faebca4e14ed4a670300a714e 100644 (file)
@@ -26,6 +26,8 @@
 
 #include "../json/json.h"
 #include <cstdint>
+#include <vector>
+#include <string>
 
 namespace vulkan_cpu
 {
@@ -35,23 +37,136 @@ namespace ast
 {
 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
@@ -79,6 +194,7 @@ struct Top_level
           operand_kinds(std::move(operand_kinds))
     {
     }
+    json::ast::Value to_json() const;
 };
 }
 }
index eb48aeeb7570c00fa3394f2567d1627ab1d741dc..3bfb0ebde520914622c4debe5cb2ccbf1351ebed 100644 (file)
@@ -25,6 +25,7 @@
 #include "../json/parser.h"
 #include "parser.h"
 #include "../util/optional.h"
+#include <fstream>
 
 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;
         }
     }
index ddaa34aa5fe9a4bd6b6cb9d38d6303b60128d451..7544b1b2ef446f57789929a77e4d85df5c054d43 100644 (file)
@@ -53,72 +53,64 @@ std::string Path::to_string() const
 
 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 &copyright_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')
@@ -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 <typename T>
 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<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;
@@ -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<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 &copyright_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;
@@ -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"));
 }
 }
 }
index 9ef3035401f63f626d6bc3ca5c8200940844eac8..d4f91f2aa49d5bae7d4c99b63adbfb2383e1c044 100644 (file)
@@ -31,6 +31,7 @@
 #include <vector>
 #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))
     {
     }
 };
index 4fd34f88e0e3f2839cfaca578fd58a81af183656..c142b58897556d5f080e15d92492438cf42b8080 100644 (file)
@@ -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
index ad1582dd53d2dcab952c1a89d6db9a2332abaf66..26546760155606d109a9570698880f8c56377121 100644 (file)
@@ -168,17 +168,23 @@ constexpr std::size_t max_integer_buffer_size = 64; // max number of digits is b
 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]);
 }
@@ -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
index d32acdc343bfd7e49ed65f7ce134bddbf377e5d6..9350e1866402dee58807fabf2dc2ccb2593f7de9 100644 (file)
@@ -34,6 +34,7 @@
 #include <type_traits>
 #include <cassert>
 #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<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
 {
@@ -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<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));
     }
@@ -385,6 +455,16 @@ struct Object final : public Composite_value
     }
 };
 
+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;
@@ -400,7 +480,7 @@ struct Array final : public Composite_value
         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
@@ -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<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)))
+{
 }
 }
 
@@ -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 (file)
index 0000000..7220314
--- /dev/null
@@ -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 (file)
index 0000000..2ffecb7
--- /dev/null
@@ -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 <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_ */
index 36ed166d5e450598252a2e76173a4963ea44426a..e884a93053d5372d2ff519fc4a5042fa16efaa16 100644 (file)
@@ -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 <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
@@ -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<double>::quiet_NaN();
+                    token_value =
+                        ast::Value(token_location, std::numeric_limits<double>::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<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;
                 }
@@ -473,7 +317,7 @@ public:
                 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() == '\''))
@@ -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<ast::Value> 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<std::string, ast::Value> 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<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();
@@ -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;
index ddc3ae58e66ae8f0d9f10ca8de11ecdcea7e4c14..dc147710665e589b86bda3b01a943c9e93665cf6 100644 (file)
 #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)
     {
     }
 };
diff --git a/src/json/source.cpp b/src/json/source.cpp
new file mode 100644 (file)
index 0000000..dc0d7ad
--- /dev/null
@@ -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 <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
diff --git a/src/json/source.h b/src/json/source.h
new file mode 100644 (file)
index 0000000..a72184f
--- /dev/null
@@ -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 <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_ */