feat: базовая структура проекта; docs: грамматика формы бэкуса-наура

This commit is contained in:
ShishkaDanil
2026-01-03 19:31:30 +03:00
parent ab2acdc698
commit 06164cff09
20 changed files with 2291 additions and 11 deletions

202
src/utf8.rs Normal file
View File

@@ -0,0 +1,202 @@
//! Модуль обработки UTF-8 последовательностей для no_std окружения.
//!
//! Обеспечивает декодирование UTF-8 байтов в Unicode кодпоинты
//! без использования стандартной библиотеки.
/// Результат декодирования UTF-8 символа.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecodeResult {
/// Успешно декодирован символ
/// - первый параметр: Unicode кодпоинт (char)
/// - второй параметр: количество байт, использованных для декодирования (1-4)
Ok(char, usize),
/// Неполная последовательность (нужно больше байт)
Incomplete,
/// Некорректная UTF-8 последовательность
Invalid,
}
/// Декодирует следующий UTF-8 символ из байтового буфера.
///
/// # Параметры
/// - `bytes`: слайс байтов, начинающийся с первого байта UTF-8 последовательности
///
/// # Возвращает
/// - `DecodeResult::Ok(char, usize)`: успешно декодированный символ и количество использованных байт
/// - `DecodeResult::Incomplete`: последовательность неполная (нужно больше байт)
/// - `DecodeResult::Invalid`: некорректная UTF-8 последовательность
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::utf8::{decode_utf8, DecodeResult};
///
/// let bytes = "Ъ".as_bytes();
/// match decode_utf8(bytes) {
/// DecodeResult::Ok(ch, len) => {
/// assert_eq!(ch, 'Ъ');
/// assert_eq!(len, 2);
/// }
/// _ => panic!("Ожидался успешный результат"),
/// }
/// ```
pub fn decode_utf8(bytes: &[u8]) -> DecodeResult {
// TODO: Реализовать декодирование UTF-8 без использования std
// Алгоритм:
// 1. Проверить первый байт для определения длины последовательности
// 2. Проверить, что в буфере достаточно байт
// 3. Проверить корректность старших бит последующих байт
// 4. Вычислить кодпоинт и вернуть DecodeResult::Ok
// 5. Вернуть DecodeResult::Incomplete если не хватает байт
// 6. Вернуть DecodeResult::Invalid при некорректной последовательности
if bytes.is_empty() {
return DecodeResult::Incomplete;
}
let first = bytes[0];
// ASCII символ (0xxxxxxx)
if first & 0x80 == 0 {
return DecodeResult::Ok(first as char, 1);
}
// Двухбайтовая последовательность (110xxxxx 10xxxxxx)
if first & 0xE0 == 0xC0 {
if bytes.len() < 2 {
return DecodeResult::Incomplete;
}
if bytes[1] & 0xC0 != 0x80 {
return DecodeResult::Invalid;
}
let code_point = ((first & 0x1F) as u32) << 6 | ((bytes[1] & 0x3F) as u32);
if code_point < 0x80 {
return DecodeResult::Invalid; // Overlong encoding
}
if let Some(ch) = core::char::from_u32(code_point) {
return DecodeResult::Ok(ch, 2);
}
return DecodeResult::Invalid;
}
// Трехбайтовая последовательность (1110xxxx 10xxxxxx 10xxxxxx)
if first & 0xF0 == 0xE0 {
if bytes.len() < 3 {
return DecodeResult::Incomplete;
}
if bytes[1] & 0xC0 != 0x80 || bytes[2] & 0xC0 != 0x80 {
return DecodeResult::Invalid;
}
let code_point = ((first & 0x0F) as u32) << 12
| ((bytes[1] & 0x3F) as u32) << 6
| ((bytes[2] & 0x3F) as u32);
if code_point < 0x800 {
return DecodeResult::Invalid; // Overlong encoding
}
if let Some(ch) = core::char::from_u32(code_point) {
return DecodeResult::Ok(ch, 3);
}
return DecodeResult::Invalid;
}
// Четырехбайтовая последовательность (11110xxx 10xxxxxx 10xxxxxx 10xxxxxx)
if first & 0xF8 == 0xF0 {
if bytes.len() < 4 {
return DecodeResult::Incomplete;
}
if bytes[1] & 0xC0 != 0x80 || bytes[2] & 0xC0 != 0x80 || bytes[3] & 0xC0 != 0x80 {
return DecodeResult::Invalid;
}
let code_point = ((first & 0x07) as u32) << 18
| ((bytes[1] & 0x3F) as u32) << 12
| ((bytes[2] & 0x3F) as u32) << 6
| ((bytes[3] & 0x3F) as u32);
if code_point < 0x10000 || code_point > 0x10FFFF {
return DecodeResult::Invalid;
}
if let Some(ch) = core::char::from_u32(code_point) {
return DecodeResult::Ok(ch, 4);
}
return DecodeResult::Invalid;
}
DecodeResult::Invalid
}
/// Итератор по UTF-8 символам в байтовом слайсе.
///
/// Позволяет последовательно декодировать UTF-8 символы из байтового буфера.
pub struct Utf8Chars<'a> {
bytes: &'a [u8],
position: usize,
}
impl<'a> Utf8Chars<'a> {
/// Создает новый итератор UTF-8 символов.
///
/// # Параметры
/// - `bytes`: байтовый слайс для декодирования
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::utf8::Utf8Chars;
///
/// let text = "Ъ+";
/// let mut chars = Utf8Chars::new(text.as_bytes());
/// ```
pub fn new(bytes: &'a [u8]) -> Self {
// TODO: Реализовать создание итератора
Self {
bytes,
position: 0,
}
}
}
impl<'a> Iterator for Utf8Chars<'a> {
type Item = Result<char, DecodeError>;
fn next(&mut self) -> Option<Self::Item> {
// TODO: Реализовать итерацию по UTF-8 символам
if self.position >= self.bytes.len() {
return None;
}
let remaining = &self.bytes[self.position..];
match decode_utf8(remaining) {
DecodeResult::Ok(ch, len) => {
self.position += len;
Some(Ok(ch))
}
DecodeResult::Incomplete => {
// Неполная последовательность в конце буфера
None
}
DecodeResult::Invalid => {
self.position += 1; // Пропускаем некорректный байт
Some(Err(DecodeError::InvalidUtf8))
}
}
}
}
/// Ошибка декодирования UTF-8.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecodeError {
/// Некорректная UTF-8 последовательность
InvalidUtf8,
}
#[cfg(test)]
mod tests {
use super::*;
// TODO: Написать тесты для UTF-8 декодирования
// - Тест декодирования ASCII символов
// - Тест декодирования двухбайтовых последовательностей (кириллица)
// - Тест декодирования трехбайтовых последовательностей
// - Тест декодирования четырехбайтовых последовательностей
// - Тест обработки неполных последовательностей
// - Тест обработки некорректных последовательностей
// - Тест итератора Utf8Chars
}