feat(furlang): add serialization
This commit is contained in:
@@ -24,7 +24,7 @@ struct lexer_error {
|
|||||||
std::string message;
|
std::string message;
|
||||||
};
|
};
|
||||||
|
|
||||||
using token_r = furlang::result<token, lexer_error>;
|
using token_r = furlang::result<lexer_error, token>;
|
||||||
|
|
||||||
class lexer {
|
class lexer {
|
||||||
public:
|
public:
|
||||||
|
|||||||
+4
-4
@@ -19,7 +19,7 @@ token_r lexer::next_token() {
|
|||||||
// TODO: Add support for single-line comments
|
// TODO: Add support for single-line comments
|
||||||
// TODO: Add support for multi-line comments
|
// TODO: Add support for multi-line comments
|
||||||
|
|
||||||
if (m_cursor >= m_content.size()) return token_r{ lexer_error{ lexer_error::EndOfFile, location() } };
|
if (m_cursor >= m_content.size()) return token_r::error(lexer_error{ lexer_error::EndOfFile, location() });
|
||||||
|
|
||||||
// TODO: Add support for negative integers (I am positive thanks to stasiu :v:)
|
// TODO: Add support for negative integers (I am positive thanks to stasiu :v:)
|
||||||
// TODO: Add support for hexadecimal and binary numeric literals
|
// TODO: Add support for hexadecimal and binary numeric literals
|
||||||
@@ -93,9 +93,9 @@ token_r lexer::next_token() {
|
|||||||
case '.': ++m_cursor; return { token::Dot };
|
case '.': ++m_cursor; return { token::Dot };
|
||||||
case ':': ++m_cursor; return { token::Colon };
|
case ':': ++m_cursor; return { token::Colon };
|
||||||
default:
|
default:
|
||||||
return token_r{
|
return token_r::error(lexer_error{ lexer_error::UnknownCharacter,
|
||||||
lexer_error{ lexer_error::UnknownCharacter, location(), "Unknown character '"s + m_content[m_cursor] + "'" }
|
location(),
|
||||||
};
|
"Unknown character '"s + m_content[m_cursor] + "'" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
#ifndef FURLANG_SERIALIZATION_CODEC_HPP
|
||||||
|
#define FURLANG_SERIALIZATION_CODEC_HPP
|
||||||
|
|
||||||
|
#include "furlang/result.hpp"
|
||||||
|
#include "furlang/serialization/io.hpp"
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace furlang {
|
||||||
|
namespace serialization {
|
||||||
|
|
||||||
|
template <typename Codec, typename T>
|
||||||
|
using codec_encode_result_t = decltype(std::declval<Codec>().encode(std::declval<writer&>(), std::declval<const T&>()));
|
||||||
|
|
||||||
|
template <typename Codec, typename T>
|
||||||
|
using codec_decode_result_t = decltype(std::declval<Codec>().decode(std::declval<reader&>()));
|
||||||
|
|
||||||
|
template <typename Codec, typename T, typename = void>
|
||||||
|
struct is_codec : std::false_type {};
|
||||||
|
|
||||||
|
template <typename Codec, typename T>
|
||||||
|
struct is_codec<Codec, T, std::void_t<codec_encode_result_t<Codec, T>, codec_decode_result_t<Codec, T>>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
template <typename Codec, typename T>
|
||||||
|
constexpr bool is_codec_v = is_codec<Codec, T>::value;
|
||||||
|
|
||||||
|
template <typename Codec, typename T, typename = std::enable_if_t<is_codec_v<Codec, T>>>
|
||||||
|
result<error> encode(Codec& codec, writer& writer, const T& value) {
|
||||||
|
return codec.encode(writer, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Codec, typename T, typename = std::enable_if_t<is_codec_v<Codec, T>>>
|
||||||
|
result<error, T> decode(Codec& codec, reader& reader) {
|
||||||
|
return codec.decode(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = void>
|
||||||
|
class codec;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct codec<T, std::enable_if_t<std::is_integral_v<T>>> {
|
||||||
|
result<error> encode(writer& writer, const T& value) { return writer.write_int(value); }
|
||||||
|
result<error, T> decode(reader& reader) { return reader.read_int(T{}); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct codec<std::string> {
|
||||||
|
result<error> encode(writer& writer, const std::string& value) { return writer.write_string(value); }
|
||||||
|
|
||||||
|
result<error, std::string> decode(reader& reader) { return reader.read_string(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace serialization
|
||||||
|
} // namespace furlang
|
||||||
|
|
||||||
|
#endif // FURLANG_SERIALIZATION_CODEC_HPP
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef FURLANG_SERIALIZATION_ERROR_HPP
|
||||||
|
#define FURLANG_SERIALIZATION_ERROR_HPP
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace furlang {
|
||||||
|
namespace serialization {
|
||||||
|
|
||||||
|
enum class error_code {
|
||||||
|
EndOfFile,
|
||||||
|
InvalidData,
|
||||||
|
InvalidTag,
|
||||||
|
InvalidVersion,
|
||||||
|
IntegerOverflow,
|
||||||
|
SizeLimit,
|
||||||
|
DuplicateId,
|
||||||
|
UnknownId,
|
||||||
|
TypeMismatch,
|
||||||
|
Unsupported,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct error {
|
||||||
|
error_code code;
|
||||||
|
std::string message;
|
||||||
|
|
||||||
|
std::size_t offset = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace serialization
|
||||||
|
} // namespace furlang
|
||||||
|
|
||||||
|
#endif // FURLANG_SERIALIZATION_ERROR_HPP
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
#ifndef FURLANG_SERIALIZATION_IO_HPP
|
||||||
|
#define FURLANG_SERIALIZATION_IO_HPP
|
||||||
|
|
||||||
|
#include "furlang/result.hpp"
|
||||||
|
#include "furlang/serialization/error.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace furlang {
|
||||||
|
namespace serialization {
|
||||||
|
|
||||||
|
class writer {
|
||||||
|
public:
|
||||||
|
writer() = default;
|
||||||
|
virtual ~writer() = default;
|
||||||
|
|
||||||
|
writer(writer&&) noexcept = default;
|
||||||
|
writer& operator=(writer&&) noexcept = default;
|
||||||
|
writer(const writer&) = default;
|
||||||
|
writer& operator=(const writer&) = default;
|
||||||
|
public:
|
||||||
|
virtual result<error> write_s8(std::int8_t value) = 0;
|
||||||
|
virtual result<error> write_u8(std::uint8_t value) = 0;
|
||||||
|
virtual result<error> write_s16(std::int16_t value) = 0;
|
||||||
|
virtual result<error> write_u16(std::uint16_t value) = 0;
|
||||||
|
virtual result<error> write_s32(std::int32_t value) = 0;
|
||||||
|
virtual result<error> write_u32(std::uint32_t value) = 0;
|
||||||
|
virtual result<error> write_s64(std::int64_t value) = 0;
|
||||||
|
virtual result<error> write_u64(std::uint64_t value) = 0;
|
||||||
|
|
||||||
|
result<error> write_int(std::int8_t value) { return write_s8(value); }
|
||||||
|
result<error> write_int(std::uint8_t value) { return write_u8(value); }
|
||||||
|
result<error> write_int(std::int16_t value) { return write_s16(value); }
|
||||||
|
result<error> write_int(std::uint16_t value) { return write_u16(value); }
|
||||||
|
result<error> write_int(std::int32_t value) { return write_s32(value); }
|
||||||
|
result<error> write_int(std::uint32_t value) { return write_u32(value); }
|
||||||
|
result<error> write_int(std::int64_t value) { return write_s64(value); }
|
||||||
|
result<error> write_int(std::uint64_t value) { return write_u64(value); }
|
||||||
|
|
||||||
|
virtual result<error> write_string(const char* string) = 0;
|
||||||
|
virtual result<error> write_string(std::string_view string) = 0;
|
||||||
|
virtual result<error> write_string(const std::string& string) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class reader {
|
||||||
|
public:
|
||||||
|
reader() = default;
|
||||||
|
virtual ~reader() = default;
|
||||||
|
|
||||||
|
reader(reader&&) noexcept = default;
|
||||||
|
reader& operator=(reader&&) noexcept = default;
|
||||||
|
reader(const reader&) = default;
|
||||||
|
reader& operator=(const reader&) = default;
|
||||||
|
public:
|
||||||
|
virtual result<error, std::int8_t> read_s8() = 0;
|
||||||
|
virtual result<error, std::uint8_t> read_u8() = 0;
|
||||||
|
virtual result<error, std::int16_t> read_s16() = 0;
|
||||||
|
virtual result<error, std::uint16_t> read_u16() = 0;
|
||||||
|
virtual result<error, std::int32_t> read_s32() = 0;
|
||||||
|
virtual result<error, std::uint32_t> read_u32() = 0;
|
||||||
|
virtual result<error, std::int64_t> read_s64() = 0;
|
||||||
|
virtual result<error, std::uint64_t> read_u64() = 0;
|
||||||
|
|
||||||
|
result<error, std::int8_t> read_int(std::int8_t) { return read_s8(); }
|
||||||
|
result<error, std::uint8_t> read_int(std::uint8_t) { return read_u8(); }
|
||||||
|
result<error, std::int16_t> read_int(std::int16_t) { return read_s16(); }
|
||||||
|
result<error, std::uint16_t> read_int(std::uint16_t) { return read_u16(); }
|
||||||
|
result<error, std::int32_t> read_int(std::int32_t) { return read_s32(); }
|
||||||
|
result<error, std::uint32_t> read_int(std::uint32_t) { return read_u32(); }
|
||||||
|
result<error, std::int64_t> read_int(std::int64_t) { return read_s64(); }
|
||||||
|
result<error, std::uint64_t> read_int(std::uint64_t) { return read_u64(); }
|
||||||
|
|
||||||
|
virtual result<error, std::string> read_string() = 0;
|
||||||
|
|
||||||
|
virtual std::size_t offset() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class endianness {
|
||||||
|
Little = 0,
|
||||||
|
Big = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
class byte_writer : public writer {
|
||||||
|
public:
|
||||||
|
byte_writer(endianness endianness = endianness::Big)
|
||||||
|
: m_endianness(endianness) {}
|
||||||
|
public:
|
||||||
|
result<error> write_s8(std::int8_t value) override;
|
||||||
|
result<error> write_u8(std::uint8_t value) override;
|
||||||
|
result<error> write_s16(std::int16_t value) override;
|
||||||
|
result<error> write_u16(std::uint16_t value) override;
|
||||||
|
result<error> write_s32(std::int32_t value) override;
|
||||||
|
result<error> write_u32(std::uint32_t value) override;
|
||||||
|
result<error> write_s64(std::int64_t value) override;
|
||||||
|
result<error> write_u64(std::uint64_t value) override;
|
||||||
|
|
||||||
|
result<error> write_string(const char* string) override;
|
||||||
|
result<error> write_string(std::string_view string) override;
|
||||||
|
result<error> write_string(const std::string& string) override;
|
||||||
|
private:
|
||||||
|
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
|
||||||
|
result<error> write_integral_le(T value) {
|
||||||
|
return write_integral_le(value, std::make_index_sequence<sizeof(T)>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>, std::size_t... I>
|
||||||
|
result<error> write_integral_le(T value, std::index_sequence<I...>) {
|
||||||
|
auto usig = static_cast<std::make_unsigned_t<T>>(value);
|
||||||
|
(m_bytes.push_back(usig >> (I * 8)), ...);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
|
||||||
|
void write_integral_be(T value) {
|
||||||
|
write_integral_be(value, std::make_index_sequence<sizeof(T)>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>, std::size_t... I>
|
||||||
|
void write_integral_be(T value, std::index_sequence<I...>) {
|
||||||
|
auto usig = static_cast<std::make_unsigned_t<T>>(value);
|
||||||
|
(m_bytes.push_back(usig >> ((sizeof(T) - 1 - I) * 8)), ...);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
endianness m_endianness;
|
||||||
|
std::vector<std::uint8_t> m_bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
class byte_reader : public reader {
|
||||||
|
public:
|
||||||
|
byte_reader(const std::uint8_t* bytes, std::size_t length, endianness endianness = endianness::Big)
|
||||||
|
: m_endianness(endianness), m_bytes(bytes), m_length(length) {}
|
||||||
|
public:
|
||||||
|
result<error, std::int8_t> read_s8() override;
|
||||||
|
result<error, std::uint8_t> read_u8() override;
|
||||||
|
result<error, std::int16_t> read_s16() override;
|
||||||
|
result<error, std::uint16_t> read_u16() override;
|
||||||
|
result<error, std::int32_t> read_s32() override;
|
||||||
|
result<error, std::uint32_t> read_u32() override;
|
||||||
|
result<error, std::int64_t> read_s64() override;
|
||||||
|
result<error, std::uint64_t> read_u64() override;
|
||||||
|
|
||||||
|
result<error, std::string> read_string() override;
|
||||||
|
|
||||||
|
std::size_t offset() const override;
|
||||||
|
private:
|
||||||
|
result<error, std::uint8_t> read_byte() {
|
||||||
|
if (m_offset >= m_length)
|
||||||
|
return result<error, std::uint8_t>::error(error{ error_code::EndOfFile, "", m_offset });
|
||||||
|
return { m_bytes[m_offset++] };
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
|
||||||
|
result<error, T> read_integral_le() {
|
||||||
|
using U = std::make_unsigned_t<T>;
|
||||||
|
U usig = 0;
|
||||||
|
for (std::size_t i = 0; i < sizeof(T); ++i) {
|
||||||
|
auto res = read_byte();
|
||||||
|
if (res.has_error()) return res;
|
||||||
|
usig |= static_cast<U>(res.value()) << (i * 8);
|
||||||
|
}
|
||||||
|
return { static_cast<T>(usig) };
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
|
||||||
|
result<error, T> read_integral_be() {
|
||||||
|
using U = std::make_unsigned_t<T>;
|
||||||
|
U usig = 0;
|
||||||
|
for (std::size_t i = 0; i < sizeof(T); ++i) {
|
||||||
|
auto res = read_byte();
|
||||||
|
if (res.has_error()) return res;
|
||||||
|
usig |= static_cast<U>(res.value()) << ((sizeof(T) - 1 - i) * 8);
|
||||||
|
}
|
||||||
|
return { static_cast<T>(usig) };
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
endianness m_endianness;
|
||||||
|
const std::uint8_t* m_bytes;
|
||||||
|
std::size_t m_length;
|
||||||
|
std::size_t m_offset = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace serialization
|
||||||
|
} // namespace furlang
|
||||||
|
|
||||||
|
#endif // FURLANG_SERIALIZATION_IO_HPP
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
#include "furlang/serialization/io.hpp"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace furlang::serialization {
|
||||||
|
|
||||||
|
result<error> byte_writer::write_s8(std::int8_t value) {
|
||||||
|
if (m_endianness == endianness::Little)
|
||||||
|
write_integral_le(value);
|
||||||
|
else
|
||||||
|
write_integral_be(value);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error> byte_writer::write_u8(std::uint8_t value) {
|
||||||
|
if (m_endianness == endianness::Little)
|
||||||
|
write_integral_le(value);
|
||||||
|
else
|
||||||
|
write_integral_be(value);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error> byte_writer::write_s16(std::int16_t value) {
|
||||||
|
if (m_endianness == endianness::Little)
|
||||||
|
write_integral_le(value);
|
||||||
|
else
|
||||||
|
write_integral_be(value);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error> byte_writer::write_u16(std::uint16_t value) {
|
||||||
|
if (m_endianness == endianness::Little)
|
||||||
|
write_integral_le(value);
|
||||||
|
else
|
||||||
|
write_integral_be(value);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error> byte_writer::write_s32(std::int32_t value) {
|
||||||
|
if (m_endianness == endianness::Little)
|
||||||
|
write_integral_le(value);
|
||||||
|
else
|
||||||
|
write_integral_be(value);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error> byte_writer::write_u32(std::uint32_t value) {
|
||||||
|
if (m_endianness == endianness::Little)
|
||||||
|
write_integral_le(value);
|
||||||
|
else
|
||||||
|
write_integral_be(value);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error> byte_writer::write_s64(std::int64_t value) {
|
||||||
|
if (m_endianness == endianness::Little)
|
||||||
|
write_integral_le(value);
|
||||||
|
else
|
||||||
|
write_integral_be(value);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error> byte_writer::write_u64(std::uint64_t value) {
|
||||||
|
if (m_endianness == endianness::Little)
|
||||||
|
write_integral_le(value);
|
||||||
|
else
|
||||||
|
write_integral_be(value);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error> byte_writer::write_string(const char* string) {
|
||||||
|
write_u16(std::strlen(string));
|
||||||
|
m_bytes.insert(m_bytes.end(), string, string + std::strlen(string));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error> byte_writer::write_string(std::string_view string) {
|
||||||
|
write_u16(string.length());
|
||||||
|
m_bytes.insert(m_bytes.end(), string.begin(), string.end());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error> byte_writer::write_string(const std::string& string) {
|
||||||
|
write_u16(string.length());
|
||||||
|
m_bytes.insert(m_bytes.end(), string.begin(), string.end());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error, std::int8_t> byte_reader::read_s8() {
|
||||||
|
return m_endianness == endianness::Little ? read_integral_le<std::int8_t>() : read_integral_be<std::int8_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error, std::uint8_t> byte_reader::read_u8() {
|
||||||
|
return m_endianness == endianness::Little ? read_integral_le<std::uint8_t>() : read_integral_be<std::uint8_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error, std::int16_t> byte_reader::read_s16() {
|
||||||
|
return m_endianness == endianness::Little ? read_integral_le<std::int16_t>() : read_integral_be<std::int16_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error, std::uint16_t> byte_reader::read_u16() {
|
||||||
|
return m_endianness == endianness::Little ? read_integral_le<std::uint16_t>() : read_integral_be<std::uint16_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error, std::int32_t> byte_reader::read_s32() {
|
||||||
|
return m_endianness == endianness::Little ? read_integral_le<std::int32_t>() : read_integral_be<std::int32_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error, std::uint32_t> byte_reader::read_u32() {
|
||||||
|
return m_endianness == endianness::Little ? read_integral_le<std::uint32_t>() : read_integral_be<std::uint32_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error, std::int64_t> byte_reader::read_s64() {
|
||||||
|
return m_endianness == endianness::Little ? read_integral_le<std::int64_t>() : read_integral_be<std::int64_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error, std::uint64_t> byte_reader::read_u64() {
|
||||||
|
return m_endianness == endianness::Little ? read_integral_le<std::uint64_t>() : read_integral_be<std::uint64_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
result<error, std::string> byte_reader::read_string() {
|
||||||
|
std::string str;
|
||||||
|
auto length = read_u16();
|
||||||
|
if (length.has_error()) return length;
|
||||||
|
str.resize(length.value());
|
||||||
|
if (m_offset + str.length() >= m_length)
|
||||||
|
return result<error, std::string>::error(error{ error_code::EndOfFile, "", m_length });
|
||||||
|
str.assign(reinterpret_cast<const char*>(m_bytes) + m_offset, str.length());
|
||||||
|
m_offset += str.length();
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t byte_reader::offset() const {
|
||||||
|
return m_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace furlang::serialization
|
||||||
Reference in New Issue
Block a user