diff --git a/furas/include/furas/lexer.hpp b/furas/include/furas/lexer.hpp index ba3297e..e21a642 100644 --- a/furas/include/furas/lexer.hpp +++ b/furas/include/furas/lexer.hpp @@ -24,7 +24,7 @@ struct lexer_error { std::string message; }; -using token_r = furlang::result; +using token_r = furlang::result; class lexer { public: diff --git a/furas/src/lexer.cpp b/furas/src/lexer.cpp index fcb1b64..ffa17c5 100644 --- a/furas/src/lexer.cpp +++ b/furas/src/lexer.cpp @@ -19,7 +19,7 @@ token_r lexer::next_token() { // TODO: Add support for single-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 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::Colon }; default: - return token_r{ - lexer_error{ lexer_error::UnknownCharacter, location(), "Unknown character '"s + m_content[m_cursor] + "'" } - }; + return token_r::error(lexer_error{ lexer_error::UnknownCharacter, + location(), + "Unknown character '"s + m_content[m_cursor] + "'" }); } } diff --git a/furlang/include/furlang/serialization/codec.hpp b/furlang/include/furlang/serialization/codec.hpp new file mode 100644 index 0000000..6a6e1c0 --- /dev/null +++ b/furlang/include/furlang/serialization/codec.hpp @@ -0,0 +1,58 @@ +#ifndef FURLANG_SERIALIZATION_CODEC_HPP +#define FURLANG_SERIALIZATION_CODEC_HPP + +#include "furlang/result.hpp" +#include "furlang/serialization/io.hpp" + +#include +#include + +namespace furlang { +namespace serialization { + +template +using codec_encode_result_t = decltype(std::declval().encode(std::declval(), std::declval())); + +template +using codec_decode_result_t = decltype(std::declval().decode(std::declval())); + +template +struct is_codec : std::false_type {}; + +template +struct is_codec, codec_decode_result_t>> + : std::true_type {}; + +template +constexpr bool is_codec_v = is_codec::value; + +template >> +result encode(Codec& codec, writer& writer, const T& value) { + return codec.encode(writer, value); +} + +template >> +result decode(Codec& codec, reader& reader) { + return codec.decode(reader); +} + +template +class codec; + +template +struct codec>> { + result encode(writer& writer, const T& value) { return writer.write_int(value); } + result decode(reader& reader) { return reader.read_int(T{}); } +}; + +template <> +struct codec { + result encode(writer& writer, const std::string& value) { return writer.write_string(value); } + + result decode(reader& reader) { return reader.read_string(); } +}; + +} // namespace serialization +} // namespace furlang + +#endif // FURLANG_SERIALIZATION_CODEC_HPP diff --git a/furlang/include/furlang/serialization/error.hpp b/furlang/include/furlang/serialization/error.hpp new file mode 100644 index 0000000..0957de2 --- /dev/null +++ b/furlang/include/furlang/serialization/error.hpp @@ -0,0 +1,33 @@ +#ifndef FURLANG_SERIALIZATION_ERROR_HPP +#define FURLANG_SERIALIZATION_ERROR_HPP + +#include +#include + +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 diff --git a/furlang/include/furlang/serialization/io.hpp b/furlang/include/furlang/serialization/io.hpp new file mode 100644 index 0000000..6805acf --- /dev/null +++ b/furlang/include/furlang/serialization/io.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 +#include +#include +#include +#include +#include + +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 write_s8(std::int8_t value) = 0; + virtual result write_u8(std::uint8_t value) = 0; + virtual result write_s16(std::int16_t value) = 0; + virtual result write_u16(std::uint16_t value) = 0; + virtual result write_s32(std::int32_t value) = 0; + virtual result write_u32(std::uint32_t value) = 0; + virtual result write_s64(std::int64_t value) = 0; + virtual result write_u64(std::uint64_t value) = 0; + + result write_int(std::int8_t value) { return write_s8(value); } + result write_int(std::uint8_t value) { return write_u8(value); } + result write_int(std::int16_t value) { return write_s16(value); } + result write_int(std::uint16_t value) { return write_u16(value); } + result write_int(std::int32_t value) { return write_s32(value); } + result write_int(std::uint32_t value) { return write_u32(value); } + result write_int(std::int64_t value) { return write_s64(value); } + result write_int(std::uint64_t value) { return write_u64(value); } + + virtual result write_string(const char* string) = 0; + virtual result write_string(std::string_view string) = 0; + virtual result 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 read_s8() = 0; + virtual result read_u8() = 0; + virtual result read_s16() = 0; + virtual result read_u16() = 0; + virtual result read_s32() = 0; + virtual result read_u32() = 0; + virtual result read_s64() = 0; + virtual result read_u64() = 0; + + result read_int(std::int8_t) { return read_s8(); } + result read_int(std::uint8_t) { return read_u8(); } + result read_int(std::int16_t) { return read_s16(); } + result read_int(std::uint16_t) { return read_u16(); } + result read_int(std::int32_t) { return read_s32(); } + result read_int(std::uint32_t) { return read_u32(); } + result read_int(std::int64_t) { return read_s64(); } + result read_int(std::uint64_t) { return read_u64(); } + + virtual result 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 write_s8(std::int8_t value) override; + result write_u8(std::uint8_t value) override; + result write_s16(std::int16_t value) override; + result write_u16(std::uint16_t value) override; + result write_s32(std::int32_t value) override; + result write_u32(std::uint32_t value) override; + result write_s64(std::int64_t value) override; + result write_u64(std::uint64_t value) override; + + result write_string(const char* string) override; + result write_string(std::string_view string) override; + result write_string(const std::string& string) override; +private: + template >> + result write_integral_le(T value) { + return write_integral_le(value, std::make_index_sequence{}); + } + + template >, std::size_t... I> + result write_integral_le(T value, std::index_sequence) { + auto usig = static_cast>(value); + (m_bytes.push_back(usig >> (I * 8)), ...); + return {}; + } +private: + template >> + void write_integral_be(T value) { + write_integral_be(value, std::make_index_sequence{}); + } + + template >, std::size_t... I> + void write_integral_be(T value, std::index_sequence) { + auto usig = static_cast>(value); + (m_bytes.push_back(usig >> ((sizeof(T) - 1 - I) * 8)), ...); + } +private: + endianness m_endianness; + std::vector 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 read_s8() override; + result read_u8() override; + result read_s16() override; + result read_u16() override; + result read_s32() override; + result read_u32() override; + result read_s64() override; + result read_u64() override; + + result read_string() override; + + std::size_t offset() const override; +private: + result read_byte() { + if (m_offset >= m_length) + return result::error(error{ error_code::EndOfFile, "", m_offset }); + return { m_bytes[m_offset++] }; + } +private: + template >> + result read_integral_le() { + using U = std::make_unsigned_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(res.value()) << (i * 8); + } + return { static_cast(usig) }; + } +private: + template >> + result read_integral_be() { + using U = std::make_unsigned_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(res.value()) << ((sizeof(T) - 1 - i) * 8); + } + return { static_cast(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 diff --git a/furlang/src/serialization/io.cpp b/furlang/src/serialization/io.cpp new file mode 100644 index 0000000..3e3adb2 --- /dev/null +++ b/furlang/src/serialization/io.cpp @@ -0,0 +1,137 @@ +#include "furlang/serialization/io.hpp" + +#include + +namespace furlang::serialization { + +result byte_writer::write_s8(std::int8_t value) { + if (m_endianness == endianness::Little) + write_integral_le(value); + else + write_integral_be(value); + return {}; +} + +result byte_writer::write_u8(std::uint8_t value) { + if (m_endianness == endianness::Little) + write_integral_le(value); + else + write_integral_be(value); + return {}; +} + +result byte_writer::write_s16(std::int16_t value) { + if (m_endianness == endianness::Little) + write_integral_le(value); + else + write_integral_be(value); + return {}; +} + +result byte_writer::write_u16(std::uint16_t value) { + if (m_endianness == endianness::Little) + write_integral_le(value); + else + write_integral_be(value); + return {}; +} + +result byte_writer::write_s32(std::int32_t value) { + if (m_endianness == endianness::Little) + write_integral_le(value); + else + write_integral_be(value); + return {}; +} + +result byte_writer::write_u32(std::uint32_t value) { + if (m_endianness == endianness::Little) + write_integral_le(value); + else + write_integral_be(value); + return {}; +} + +result byte_writer::write_s64(std::int64_t value) { + if (m_endianness == endianness::Little) + write_integral_le(value); + else + write_integral_be(value); + return {}; +} + +result byte_writer::write_u64(std::uint64_t value) { + if (m_endianness == endianness::Little) + write_integral_le(value); + else + write_integral_be(value); + return {}; +} + +result 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 byte_writer::write_string(std::string_view string) { + write_u16(string.length()); + m_bytes.insert(m_bytes.end(), string.begin(), string.end()); + return {}; +} + +result byte_writer::write_string(const std::string& string) { + write_u16(string.length()); + m_bytes.insert(m_bytes.end(), string.begin(), string.end()); + return {}; +} + +result byte_reader::read_s8() { + return m_endianness == endianness::Little ? read_integral_le() : read_integral_be(); +} + +result byte_reader::read_u8() { + return m_endianness == endianness::Little ? read_integral_le() : read_integral_be(); +} + +result byte_reader::read_s16() { + return m_endianness == endianness::Little ? read_integral_le() : read_integral_be(); +} + +result byte_reader::read_u16() { + return m_endianness == endianness::Little ? read_integral_le() : read_integral_be(); +} + +result byte_reader::read_s32() { + return m_endianness == endianness::Little ? read_integral_le() : read_integral_be(); +} + +result byte_reader::read_u32() { + return m_endianness == endianness::Little ? read_integral_le() : read_integral_be(); +} + +result byte_reader::read_s64() { + return m_endianness == endianness::Little ? read_integral_le() : read_integral_be(); +} + +result byte_reader::read_u64() { + return m_endianness == endianness::Little ? read_integral_le() : read_integral_be(); +} + +result 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(error{ error_code::EndOfFile, "", m_length }); + str.assign(reinterpret_cast(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