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

24
.gitignore vendored
View File

@@ -1,14 +1,16 @@
# Generated by Cargo # Rust
# will have compiled files and executables /target/
debug/ **/*.rs.bk
target/ *.pdb
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock Cargo.lock
# These are backup files generated by rustfmt # IDE
**/*.rs.bk .idea/
.vscode/
*.swp
*.swo
*~
# MSVC Windows builds of rustc generate these, which store debugging information # OS
*.pdb .DS_Store
Thumbs.db

38
Cargo.toml Normal file
View File

@@ -0,0 +1,38 @@
[package]
name = "tverd-plus-tokenizer"
version = "0.1.0"
edition = "2021"
authors = ["Твердь Project Team"]
description = "Доверенный лексический анализатор языка Ъ+ (системный базис Ъ++). Реализация на Rust (no_std) для архитектур Эльбрус и x86."
license = "MIT OR Apache-2.0"
repository = ""
documentation = ""
readme = "README.md"
[lib]
name = "tverd_plus_tokenizer"
crate-type = ["lib"]
# Настройка no_std окружения
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
panic = 'abort'
[profile.dev]
opt-level = 0
debug = true
# Зависимости отсутствуют согласно требованиям безопасности
# (no external dynamic libraries allowed)
[dependencies]
# Опциональные зависимости для тестирования (только в dev режиме)
[dev-dependencies]
# Бенчмарки (опционально)
[[bench]]
name = "tokenizer_bench"
harness = false

View File

@@ -0,0 +1,34 @@
//! Бенчмарки производительности токенизатора.
// TODO: Добавить бенчмарки производительности
// - Бенчмарк токенизации простого кода
// - Бенчмарк токенизации сложного кода
// - Бенчмарк обработки комментариев
// - Бенчмарк обработки идентификаторов
/*
Пример использования:
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use tverd_plus_tokenizer::Tokenizer;
fn bench_tokenize_simple(c: &mut Criterion) {
let source = "Ъ+ @переменная :метка |> << Ъ-";
c.bench_function("tokenize_simple", |b| {
b.iter(|| {
let tokenizer = Tokenizer::new(black_box(source));
tokenizer.collect::<Vec<_>>()
});
});
}
criterion_group!(benches, bench_tokenize_simple);
criterion_main!(benches);
*/
fn main() {
// TODO: Реализовать бенчмарки
println!("Бенчмарки производительности токенизатора");
println!("TODO: Реализовать бенчмарки с использованием criterion");
}

262
docs/grammar.md Normal file
View File

@@ -0,0 +1,262 @@
# Грамматика языка Ъ+ (EBNF)
Формальное описание грамматики языка «Ъ+» в форме Бэкуса — Наура (EBNF).
## Основные принципы
- **Кириллица обязательна** для ключевых конструкций (блоков)
- **Латинский алфавит опционален**, используется только для имен переменных
- **Конвейерный стиль** (`|>`) — основной способ передачи данных
- **Префиксация сущностей** (`@`, `:`, `?`) для однозначного разбора
- **Разделение мутации и вычисления** — разные операторы для сдвигов и записи в буфер
## Базовые символы
```ebnf
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
hex_digit = digit | "a" | "b" | "c" | "d" | "e" | "f" | "A" | "B" | "C" | "D" | "E" | "F";
binary_digit = "0" | "1";
cyrillic_letter = "а".."я" | "А".."Я" | "ё" | "Ё";
latin_letter = "a".."z" | "A".."Z";
letter = cyrillic_letter | latin_letter;
```
## 1. Структура программы
```ebnf
program = { item };
item = function_definition | constant_definition | type_definition;
type_definition = "!:ТИП", identifier_body, "=", ...
constant_definition = "!:КОНСТ", identifier_body, "=", ...
```
## 2. Идентификаторы
```ebnf
variable_identifier = "@", identifier_body;
label_identifier = ":", identifier_body;
predicate_identifier = "?", identifier_body;
identifier_body = letter, {letter | digit | "_"};
```
## 3. Литералы
### 3.1. Числовые литералы
```ebnf
integer_literal = (decimal_literal | hex_literal | binary_literal), [type_suffix];
decimal_literal = digit, [{digit | "_"}, digit];
hex_literal = "0x", hex_digit, [{hex_digit | "_"}, hex_digit];
binary_literal = "0b", binary_digit, [{binary_digit | "_"}, binary_digit];
type_suffix = "_", type_suffix_raw;
type_suffix_raw =
"б8" | "ц8" |
"б16" | "ц16" |
"б32" | "ц32" |
"б64" | "ц64" |
"бр" | "цр" |
"в32" | "в64";
float_literal = (decimal_literal, ".", [decimal_literal] | ".", decimal_literal), [type_suffix];
```
### 3.2. Строки и коллекции
```ebnf
string_literal = '"', {string_char | escape_sequence}, '"';
string_char = ? любой символ Unicode, кроме '"' и '\' ?;
escape_sequence = "\", ("n" | "t" | "r" | "\\" | '"' | "0", hex_digit, hex_digit | "u{", hex_digit, {hex_digit}, "}");
array_literal = array_fixed | array_list;
array_fixed = "[", expression, ";", expression, "]";
array_list = "[", [expression, {",", expression}, [","]], "]";
tuple_literal = "(", [expression, {",", expression}, [","]], ")";
```
## 4. Операторы
### 4.1. Арифметические и логические
```ebnf
arithmetic_operator = "+" | "-" | "*" | "/" | "%";
logical_operator = "&&" | "||" | "!";
comparison_operator = "==" | "!=" | "<" | ">" | "<=" | ">=";
```
### 4.2. Битовые операторы
```ebnf
bitwise_operator = "&" | "|" | "^" | "~" | "<<<" | ">>>";
```
### 4.3. Специальные операторы
```ebnf
special_operator = "|>" | "<<" | "=";
```
## 5. Управляющие конструкции
### 5.1. Блоки и инструкции
```ebnf
block_start = "Ъ+";
block_end = "Ъ-";
block = block_start, {statement}, block_end;
statement =
binding_statement
| mutation_statement
| (expression, [";"]);
binding_statement = variable_identifier, "=", expression, ";";
mutation_statement = variable_identifier, "<<", expression, ";";
```
### 5.2. Условие (Предикативное ветвление)
```ebnf
conditional_expr = "?", expression, ( block_branch | arrow_branch );
block_branch = block, [":", block];
arrow_branch = "->", expression, [":", expression];
```
### 5.3. Цикл (Итеративный предикат)
```ebnf
loop_expr = "?*", expression, block;
```
### 5.4. Сопоставление с образцом
```ebnf
match_expr = "?", variable_identifier, "{", match_arm, {match_arm}, "}";
match_arm = "|", pattern, "->", (expression | block), [","];
pattern = literal | variable_identifier | "_" ;
```
## 6. Функции и Типы
### 6.1. Определение функции и Типов
```ebnf
function_definition = ":", identifier_body, [parameter_list], ["->", type_descriptor], block;
parameter_list = variable_identifier, {",", variable_identifier};
type_descriptor = type_suffix_raw | "@пусто" | array_type | tuple_type | function_type | variable_identifier;
array_type = "[", type_descriptor, ";", expression, "]";
tuple_type = "(", [type_descriptor, {",", type_descriptor}], ")";
function_type = ":", "(", [type_descriptor, {",", type_descriptor}], ")", ["->", type_descriptor];
```
### 6.2. Вызовы
```ebnf
function_call = label_identifier, "(", [argument_list], ")";
argument_list = expression, {",", expression};
```
### 6.3. Лямбды
```ebnf
lambda = "@", "(", [parameter_list], "->", (expression | block), ")";
```
## 7. Выражения и Приоритеты
```ebnf
expression = mutation_expr;
(* 13: Мутация *)
mutation_expr = binding_expr, [ "<<", mutation_expr ];
(* 12: Связывание *)
binding_expr = pipeline_expr, [ "=", binding_expr ];
(* 11: Конвейер *)
pipeline_expr = logical_or_expr, { "|>", (function_call | lambda | label_identifier) };
(* 10: Логическое ИЛИ *)
logical_or_expr = logical_and_expr, { "||", logical_and_expr };
(* 9: Логическое И *)
logical_and_expr = comparison_expr, { "&&", comparison_expr };
(* 8: Сравнение *)
comparison_expr = bitwise_or_expr, [ comparison_operator, bitwise_or_expr ];
(* 7: Битовое ИЛИ *)
bitwise_or_expr = bitwise_xor_expr, { "|", bitwise_xor_expr };
(* 6: Битовое XOR *)
bitwise_xor_expr = bitwise_and_expr, { "^", bitwise_and_expr };
(* 5: Битовое И *)
bitwise_and_expr = shift_expr, { "&", shift_expr };
(* 4: Сдвиги *)
shift_expr = additive_expr, { ("<<<" | ">>>"), additive_expr };
(* 3: Сложение / Вычитание *)
additive_expr = multiplicative_expr, { ("+" | "-"), multiplicative_expr };
(* 2: Умножение / Деление / Остаток (Уровень power_expr удален) *)
multiplicative_expr = unary_expr, { ("*" | "/" | "%"), unary_expr };
(* 1: Унарные операторы и Постфиксные выражения *)
unary_expr = ("-" | "!" | "~"), unary_expr | postfix_expr;
(* 0: Постфиксные (Индексация, Доступ к полям) и Первичные *)
postfix_expr = primary_expr, { index_access | member_access };
index_access = "[", expression, "]";
member_access = ".", identifier_body;
primary_expr =
literal
| variable_identifier
| function_call
| lambda
| parenthesized_expr
| block
| conditional_expr
| match_expr
| loop_expr;
parenthesized_expr = "(", expression, ")";
literal = integer_literal | float_literal | string_literal | array_literal | tuple_literal;
```
## 8. Комментарии
```ebnf
comment = "<[", {comment_content | comment}, "]>";
comment_content = ? любой символ, кроме последовательности "]>" и начала вложенного "<[" ?;
```
## 9. Пример кода (Валидация по грамматике)
```rust
<[ Пример функции вычисления CRC ]>
:ВычислитьРЦ @данные, @длина -> @црц Ъ+
@црц = 0xFFFFFFFF_бр;
@и = 0_бр;
?* (@и < @длина) Ъ+
@байт = @данные[@и];
@црц = @црц ^ @байт;
@к = 0_б8;
?* (@к < 8_б8) Ъ+
? (@црц & 1_бр) -> (@црц >>> 1) ^ 0xEDB88320_бр : (@црц >>> 1);
@к << @к + 1_б8;
Ъ-
@и << @и + 1_бр;
Ъ-
@црц = ~@црц;
Ъ-
```
## Примечания
- **Кодировка**: UTF-8 (ISO/IEC 10646)
- **Отслеживание позиций**: Каждый токен имеет координаты (строка, столбец) для сквозного аудита
- **Детерминизм**: Язык предназначен для создания детерминированного системного ПО
- **Безопасность**: Минимизация вектора атак, использование no_std окружения
- **Аппаратная поддержка**: Оптимизация для архитектур «Эльбрус» (предикатное исполнение) и x86

View File

@@ -0,0 +1,63 @@
//! Базовый пример использования токенизатора языка Ъ+.
// TODO: Реализовать пример использования токенизатора
// - Создание токенизатора
// - Итерация по токенам
// - Обработка ошибок
// - Вывод информации о токенах
/*
Пример использования:
use tverd_plus_tokenizer::Tokenizer;
fn main() {
let source = r#"
@порт = 0xAF42
@данные = [0; 64]
@коэф = 1.25
Ъ+
@знач = вх(@порт)
? @знач < 5 : @знач = 0
@знач = @знач * @коэф
@данные << @знач
? длина(@данные) < 64 : Ъ+
Ъ-
выдать(@данные)
"#;
let mut tokenizer = Tokenizer::new(source);
println!("Токены:");
for result in tokenizer {
match result {
Ok(token) => {
println!(
" {:?} на позиции {}:{} - '{}'",
token.kind,
token.position.line,
token.position.column,
token.lexeme
);
}
Err(error) => {
eprintln!(
"Ошибка на позиции {}:{} - {}",
error.position.line,
error.position.column,
error.description()
);
}
}
}
}
*/
fn main() {
// TODO: Реализовать пример использования токенизатора
println!("Пример использования токенизатора языка Ъ+");
println!("TODO: Реализовать демонстрацию токенизации");
}

176
src/comment.rs Normal file
View File

@@ -0,0 +1,176 @@
//! Модуль обработки вложенных комментариев.
//!
//! Обеспечивает распознавание и обработку комментариев с парными ограничителями
//! `<[` и `]>` с поддержкой рекурсивной вложенности без жесткого ограничения
//! уровня вложенности.
use crate::grammar::{COMMENT_END, COMMENT_START};
/// Состояние обработки комментариев.
///
/// Используется для отслеживания уровня вложенности комментариев
/// при лексическом анализе.
#[derive(Debug, Clone, Default)]
pub struct CommentState {
/// Текущий уровень вложенности комментариев (0 = вне комментария)
nesting_level: u32,
}
impl CommentState {
/// Создает новое состояние комментариев (вне комментария).
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::comment::CommentState;
/// let state = CommentState::new();
/// assert!(!state.is_in_comment());
/// ```
pub fn new() -> Self {
// TODO: Реализовать создание состояния
Self { nesting_level: 0 }
}
/// Проверяет, находится ли анализатор внутри комментария.
///
/// # Возвращает
/// `true`, если текущий уровень вложенности > 0
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::comment::CommentState;
/// let state = CommentState::new();
/// assert!(!state.is_in_comment());
/// ```
pub fn is_in_comment(&self) -> bool {
// TODO: Реализовать проверку нахождения в комментарии
self.nesting_level > 0
}
/// Обрабатывает начало комментария (`<[`).
///
/// Увеличивает уровень вложенности.
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::comment::CommentState;
/// let mut state = CommentState::new();
/// state.enter_comment();
/// assert!(state.is_in_comment());
/// ```
pub fn enter_comment(&mut self) {
// TODO: Реализовать вход в комментарий
self.nesting_level += 1;
}
/// Обрабатывает конец комментария (`]>`).
///
/// Уменьшает уровень вложенности.
///
/// # Возвращает
/// `Ok(())` если комментарий закрыт успешно,
/// `Err(())` если попытка закрыть комментарий вне комментария
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::comment::CommentState;
/// let mut state = CommentState::new();
/// state.enter_comment();
/// assert!(state.exit_comment().is_ok());
/// assert!(!state.is_in_comment());
/// ```
pub fn exit_comment(&mut self) -> Result<(), ()> {
// TODO: Реализовать выход из комментария
if self.nesting_level > 0 {
self.nesting_level -= 1;
Ok(())
} else {
Err(())
}
}
/// Получает текущий уровень вложенности комментариев.
///
/// # Возвращает
/// Текущий уровень вложенности (0 = вне комментария)
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::comment::CommentState;
/// let mut state = CommentState::new();
/// state.enter_comment();
/// assert_eq!(state.nesting_level(), 1);
/// ```
pub fn nesting_level(&self) -> u32 {
// TODO: Реализовать получение уровня вложенности
self.nesting_level
}
/// Сбрасывает состояние комментариев (выходит из всех вложенных комментариев).
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::comment::CommentState;
/// let mut state = CommentState::new();
/// state.enter_comment();
/// state.enter_comment();
/// state.reset();
/// assert!(!state.is_in_comment());
/// ```
pub fn reset(&mut self) {
// TODO: Реализовать сброс состояния
self.nesting_level = 0;
}
}
/// Проверяет, является ли строка началом комментария (`<[`).
///
/// # Параметры
/// - `text`: проверяемая строка
///
/// # Возвращает
/// `true`, если строка начинается с `<[`
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::comment::is_comment_start;
/// assert!(is_comment_start("<[ комментарий ]>"));
/// assert!(!is_comment_start("не комментарий"));
/// ```
pub fn is_comment_start(text: &str) -> bool {
// TODO: Реализовать проверку начала комментария
text.starts_with(COMMENT_START)
}
/// Проверяет, является ли строка концом комментария (`]>`).
///
/// # Параметры
/// - `text`: проверяемая строка
///
/// # Возвращает
/// `true`, если строка начинается с `]>`
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::comment::is_comment_end;
/// assert!(is_comment_end("]> конец"));
/// assert!(!is_comment_end("не конец"));
/// ```
pub fn is_comment_end(text: &str) -> bool {
// TODO: Реализовать проверку конца комментария
text.starts_with(COMMENT_END)
}
#[cfg(test)]
mod tests {
use super::*;
// TODO: Написать тесты для обработки комментариев
// - Тест создания CommentState
// - Тест is_in_comment
// - Тест enter_comment
// - Тест exit_comment (успешный и неуспешный)
// - Тест вложенных комментариев
// - Тест is_comment_start
// - Тест is_comment_end
}

181
src/dfa.rs Normal file
View File

@@ -0,0 +1,181 @@
//! Модуль состояний детерминированного конечного автомата (ДКА).
//!
//! Определяет состояния и переходы ДКА для распознавания токенов
//! языка Ъ+ в процессе лексического анализа.
/// Состояние детерминированного конечного автомата.
///
/// Представляет текущее состояние ДКА при обработке входного потока символов.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DfaState {
/// Начальное состояние (ожидание начала токена)
Initial,
// Состояния идентификаторов
/// Состояние обработки идентификатора переменной (после `@`)
VariableIdentifier,
/// Состояние обработки идентификатора метки (после `:`)
LabelIdentifier,
/// Состояние обработки предиката (после `?`)
PredicateIdentifier,
// Состояния блоков и комментариев
/// Состояние обработки начала оператора блока (`Ъ`)
BlockStartCandidate,
/// Состояние обработки оператора блока (`Ъ+` или `Ъ-`)
BlockOperator,
/// Состояние обработки начала комментария (после `<`)
CommentStartCandidate,
/// Состояние внутри комментария
InComment,
/// Состояние обработки конца комментария (после `]`)
CommentEndCandidate,
// Состояния операторов
/// Состояние обработки оператора конвейера (после `|`)
PipelineCandidate,
/// Состояние обработки оператора записи/сдвига (после первой `<`)
WriteOrShiftLeftCandidate,
/// Состояние обработки оператора сдвига вправо (после первой `>`)
ShiftRightCandidate,
/// Состояние обработки оператора сравнения/присваивания (после `=`)
EqualOrAssignCandidate,
/// Состояние обработки оператора неравенства (после `!`)
NotEqualCandidate,
/// Состояние обработки оператора сравнения (после `<` или `>`)
ComparisonCandidate,
/// Состояние обработки логического И/битового И (после `&`)
LogicalAndCandidate,
/// Состояние обработки логического ИЛИ/битового ИЛИ (после `|`)
LogicalOrCandidate,
/// Состояние обработки оператора мутации (после `:`)
MutateCandidate,
/// Состояние обработки оператора возведения в степень (после первой `*`)
PowerCandidate,
// Состояния литералов
/// Состояние обработки десятичного числового литерала
DecimalLiteral,
/// Состояние обработки вещественного литерала (после `.`)
FloatLiteral,
/// Состояние обработки шестнадцатеричного литерала (после `0x`)
HexLiteral,
/// Состояние обработки двоичного литерала (после `0b`)
BinaryLiteral,
/// Состояние обработки суффикса типа литерала (после `_`)
TypeSuffixCandidate,
/// Состояние обработки строкового литерала
StringLiteral,
/// Состояние обработки escape-последовательности в строке
StringEscape,
/// Состояние обработки шестнадцатеричного escape в строке
StringHexEscape,
/// Финальное состояние (токен распознан)
Accept,
/// Состояние ошибки (некорректный токен)
Error,
}
impl DfaState {
/// Проверяет, является ли состояние финальным (токен распознан).
///
/// # Возвращает
/// `true`, если состояние является финальным
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::dfa::DfaState;
/// assert!(DfaState::Accept.is_final());
/// assert!(!DfaState::Initial.is_final());
/// ```
pub fn is_final(&self) -> bool {
// TODO: Реализовать проверку финального состояния
matches!(self, DfaState::Accept)
}
/// Проверяет, является ли состояние ошибочным.
///
/// # Возвращает
/// `true`, если состояние является ошибочным
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::dfa::DfaState;
/// assert!(DfaState::Error.is_error());
/// assert!(!DfaState::Initial.is_error());
/// ```
pub fn is_error(&self) -> bool {
// TODO: Реализовать проверку ошибочного состояния
matches!(self, DfaState::Error)
}
/// Возвращает следующее состояние ДКА на основе текущего состояния и входного символа.
///
/// # Параметры
/// - `ch`: входной символ для обработки
/// - `in_comment`: флаг, указывающий, находится ли анализатор внутри комментария
///
/// # Возвращает
/// Новое состояние ДКА после обработки символа
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::dfa::DfaState;
/// let next_state = DfaState::Initial.next_state('@', false);
/// ```
pub fn next_state(&self, ch: char, in_comment: bool) -> DfaState {
// TODO: Реализовать полные переходы ДКА для всех операторов и конструкций
// Это упрощенная заглушка - реальная реализация должна обрабатывать все переходы
match (self, ch, in_comment) {
// Переходы из начального состояния
(DfaState::Initial, '@', false) => DfaState::VariableIdentifier,
(DfaState::Initial, ':', false) => DfaState::LabelIdentifier,
(DfaState::Initial, '?', false) => DfaState::PredicateIdentifier,
(DfaState::Initial, 'Ъ', false) => DfaState::BlockStartCandidate,
(DfaState::Initial, '|', false) => DfaState::PipelineCandidate,
(DfaState::Initial, '<', false) => DfaState::WriteOrShiftLeftCandidate,
(DfaState::Initial, '>', false) => DfaState::ShiftRightCandidate,
(DfaState::Initial, '=', false) => DfaState::EqualOrAssignCandidate,
(DfaState::Initial, '!', false) => DfaState::NotEqualCandidate,
(DfaState::Initial, '&', false) => DfaState::LogicalAndCandidate,
(DfaState::Initial, '"', false) => DfaState::StringLiteral,
(DfaState::Initial, '0'..='9', false) => DfaState::DecimalLiteral,
(DfaState::Initial, '+', false) | (DfaState::Initial, '-', false) |
(DfaState::Initial, '*', false) | (DfaState::Initial, '/', false) |
(DfaState::Initial, '%', false) | (DfaState::Initial, '^', false) |
(DfaState::Initial, '~', false) => DfaState::Accept,
// Переходы для операторов (упрощенные)
(DfaState::PipelineCandidate, '>', false) => DfaState::Accept,
(DfaState::WriteOrShiftLeftCandidate, '<', false) => DfaState::Accept,
(DfaState::EqualOrAssignCandidate, '=', false) => DfaState::Accept,
(DfaState::NotEqualCandidate, '=', false) => DfaState::Accept,
(DfaState::LogicalAndCandidate, '&', false) => DfaState::Accept,
(DfaState::LogicalOrCandidate, '|', false) => DfaState::Accept,
(DfaState::PowerCandidate, '*', false) => DfaState::Accept,
_ => DfaState::Error,
}
}
}
impl Default for DfaState {
fn default() -> Self {
DfaState::Initial
}
}
#[cfg(test)]
mod tests {
use super::*;
// TODO: Написать тесты для ДКА
// - Тест is_final
// - Тест is_error
// - Тест next_state для различных переходов
// - Тест переходов из начального состояния
// - Тест обработки комментариев
// - Тест обработки операторов
}

124
src/error.rs Normal file
View File

@@ -0,0 +1,124 @@
//! Модуль обработки ошибок лексического анализа.
//!
//! Определяет типы ошибок, возникающих в процессе токенизации,
//! с привязкой к позиции в исходном коде.
use crate::position::Position;
/// Тип ошибки лексического анализа.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErrorKind {
/// Неожиданный символ в исходном коде
UnexpectedChar(char),
/// Неожиданный конец файла
UnexpectedEof,
/// Некорректная UTF-8 последовательность
InvalidUtf8,
/// Несоответствие закрывающих скобок комментариев
UnmatchedCommentClose,
/// Превышена максимальная глубина вложенности комментариев
CommentNestingTooDeep,
/// Другая ошибка (с описанием)
Other(&'static str),
}
/// Ошибка лексического анализа.
///
/// Содержит информацию о типе ошибки и позиции её возникновения
/// в исходном коде.
#[derive(Debug, Clone, PartialEq)]
pub struct Error {
/// Тип ошибки
pub kind: ErrorKind,
/// Позиция в исходном коде, где произошла ошибка
pub position: Position,
/// Дополнительное сообщение об ошибке
pub message: Option<&'static str>,
}
impl Error {
/// Создает новую ошибку.
///
/// # Параметры
/// - `kind`: тип ошибки
/// - `position`: позиция возникновения ошибки
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::error::{Error, ErrorKind};
/// use tverd_plus_tokenizer::Position;
///
/// let err = Error::new(ErrorKind::UnexpectedChar('@'), Position::start());
/// ```
pub fn new(kind: ErrorKind, position: Position) -> Self {
// TODO: Реализовать создание ошибки
Self {
kind,
position,
message: None,
}
}
/// Создает новую ошибку с дополнительным сообщением.
///
/// # Параметры
/// - `kind`: тип ошибки
/// - `position`: позиция возникновения ошибки
/// - `message`: дополнительное сообщение
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::error::{Error, ErrorKind};
/// use tverd_plus_tokenizer::Position;
///
/// let err = Error::with_message(
/// ErrorKind::InvalidUtf8,
/// Position::start(),
/// "Некорректная последовательность байтов"
/// );
/// ```
pub fn with_message(kind: ErrorKind, position: Position, message: &'static str) -> Self {
// TODO: Реализовать создание ошибки с сообщением
Self {
kind,
position,
message: Some(message),
}
}
/// Возвращает человекочитаемое описание ошибки.
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::error::{Error, ErrorKind};
/// use tverd_plus_tokenizer::Position;
///
/// let err = Error::new(ErrorKind::UnexpectedChar('@'), Position::new(5, 10));
/// let description = err.description();
/// ```
pub fn description(&self) -> &'static str {
// TODO: Реализовать формирование описания ошибки
match self.kind {
ErrorKind::UnexpectedChar(_) => "Неожиданный символ",
ErrorKind::UnexpectedEof => "Неожиданный конец файла",
ErrorKind::InvalidUtf8 => "Некорректная UTF-8 последовательность",
ErrorKind::UnmatchedCommentClose => "Несоответствие закрывающих скобок комментария",
ErrorKind::CommentNestingTooDeep => "Превышена максимальная глубина вложенности комментариев",
ErrorKind::Other(msg) => msg,
}
}
}
// TODO: Реализовать trait core::fmt::Display для Error
// TODO: Реализовать trait core::error::Error для Error (если требуется)
#[cfg(test)]
mod tests {
use super::*;
// TODO: Написать тесты для Error
// - Тест создания ошибки
// - Тест создания ошибки с сообщением
// - Тест description
}

262
src/grammar.rs Normal file
View File

@@ -0,0 +1,262 @@
//! Модуль констант и правил грамматики языка Ъ+.
//!
//! Содержит определения специальных символов, операторов и ключевых слов
//! языка для использования в лексическом анализаторе.
/// Начало блока (открытие исполняемого контекста)
pub const BLOCK_START: &str = "Ъ+";
/// Конец блока (закрытие исполняемого контекста)
pub const BLOCK_END: &str = "Ъ-";
/// Оператор конвейера (передача данных между функциями)
pub const PIPELINE_OP: &str = "|>";
/// Оператор записи (инструкция заполнения буфера данных)
pub const WRITE_OP: &str = "<<";
/// Начало комментария
pub const COMMENT_START: &str = "<[";
/// Конец комментария
pub const COMMENT_END: &str = "]>";
/// Префикс идентификатора переменной (объекта данных)
pub const VAR_PREFIX: char = '@';
/// Префикс идентификатора метки управления (адресация переходов)
pub const LABEL_PREFIX: char = ':';
/// Префикс оператора логического ветвления (предиката)
pub const PREDICATE_PREFIX: char = '?';
/// Оператор цикла
pub const LOOP_OP: char = '>';
/// Арифметические операторы
pub const OP_PLUS: char = '+';
pub const OP_MINUS: char = '-';
pub const OP_MULTIPLY: char = '*';
pub const OP_DIVIDE: char = '/';
pub const OP_MODULO: char = '%';
pub const OP_POWER: &str = "**";
/// Операторы сравнения
pub const OP_EQUAL: &str = "==";
pub const OP_NOT_EQUAL: &str = "!=";
pub const OP_LESS: char = '<';
pub const OP_GREATER: char = '>';
pub const OP_LESS_EQUAL: &str = "<=";
pub const OP_GREATER_EQUAL: &str = ">=";
/// Логические операторы
pub const OP_LOGICAL_AND: &str = "&&";
pub const OP_LOGICAL_OR: &str = "||";
pub const OP_LOGICAL_NOT: char = '!';
/// Битовые операторы
pub const OP_BITWISE_AND: char = '&';
pub const OP_BITWISE_OR: char = '|';
pub const OP_BITWISE_XOR: char = '^';
pub const OP_BITWISE_NOT: char = '~';
pub const OP_SHIFT_LEFT: &str = "<<";
pub const OP_SHIFT_RIGHT: &str = ">>";
/// Операторы присваивания
pub const OP_ASSIGN: char = '=';
pub const OP_MUTATE: &str = ":=";
/// Специальные символы
pub const ARROW: &str = "->";
pub const COMMA: char = ',';
pub const SEMICOLON: char = ';';
/// Префиксы числовых литералов
pub const HEX_PREFIX: &str = "0x";
pub const BINARY_PREFIX: &str = "0b";
/// Разделитель разрядов в числовых литералах
pub const DIGIT_SEPARATOR: char = '_';
/// Допустимые типы для литералов
pub const TYPE_SUFFIXES: &[&str] = &["u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "f32", "f64"];
/// Проверяет, является ли символ префиксом идентификатора.
///
/// # Параметры
/// - `ch`: проверяемый символ
///
/// # Возвращает
/// `true`, если символ является префиксом идентификатора (`@`, `:`, `?`)
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::grammar::is_identifier_prefix;
/// assert!(is_identifier_prefix('@'));
/// assert!(is_identifier_prefix(':'));
/// assert!(is_identifier_prefix('?'));
/// assert!(!is_identifier_prefix('a'));
/// ```
pub fn is_identifier_prefix(ch: char) -> bool {
// TODO: Реализовать проверку префикса идентификатора
ch == VAR_PREFIX || ch == LABEL_PREFIX || ch == PREDICATE_PREFIX
}
/// Проверяет, является ли символ началом оператора блока.
///
/// # Параметры
/// - `ch`: проверяемый символ
///
/// # Возвращает
/// `true`, если символ может быть началом оператора блока (`Ъ`)
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::grammar::is_block_op_start;
/// assert!(is_block_op_start('Ъ'));
/// ```
pub fn is_block_op_start(ch: char) -> bool {
// TODO: Реализовать проверку начала оператора блока
ch == 'Ъ'
}
/// Проверяет, является ли символ началом комментария.
///
/// # Параметры
/// - `ch`: проверяемый символ
///
/// # Возвращает
/// `true`, если символ может быть началом комментария (`<`)
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::grammar::is_comment_start_char;
/// assert!(is_comment_start_char('<'));
/// ```
pub fn is_comment_start_char(ch: char) -> bool {
// TODO: Реализовать проверку начала комментария
ch == '<'
}
/// Проверяет, является ли символ концом комментария.
///
/// # Параметры
/// - `ch`: проверяемый символ
///
/// # Возвращает
/// `true`, если символ может быть концом комментария (`]`)
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::grammar::is_comment_end_char;
/// assert!(is_comment_end_char(']'));
/// ```
pub fn is_comment_end_char(ch: char) -> bool {
// TODO: Реализовать проверку конца комментария
ch == ']'
}
/// Проверяет, является ли символ частью идентификатора.
///
/// Идентификаторы могут содержать символы кириллического и латинского алфавитов.
///
/// # Параметры
/// - `ch`: проверяемый символ
///
/// # Возвращает
/// `true`, если символ может быть частью идентификатора
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::grammar::is_identifier_char;
/// assert!(is_identifier_char('а'));
/// assert!(is_identifier_char('a'));
/// assert!(is_identifier_char('Я'));
/// assert!(is_identifier_char('Z'));
/// ```
pub fn is_identifier_char(ch: char) -> bool {
ch.is_alphabetic() || ch == '_' || ch.is_ascii_digit()
}
/// Проверяет, является ли символ началом арифметического оператора.
pub fn is_arithmetic_op_start(ch: char) -> bool {
matches!(ch, '+' | '-' | '*' | '/' | '%')
}
/// Проверяет, является ли символ началом оператора сравнения.
pub fn is_comparison_op_start(ch: char) -> bool {
matches!(ch, '=' | '!' | '<' | '>')
}
/// Проверяет, является ли символ началом логического оператора.
pub fn is_logical_op_start(ch: char) -> bool {
matches!(ch, '&' | '|' | '!')
}
/// Проверяет, является ли символ началом битового оператора.
pub fn is_bitwise_op_start(ch: char) -> bool {
matches!(ch, '&' | '|' | '^' | '~' | '<' | '>')
}
/// Проверяет, является ли символ началом оператора присваивания.
pub fn is_assignment_op_start(ch: char) -> bool {
ch == OP_ASSIGN || ch == LABEL_PREFIX
}
/// Проверяет, является ли символ оператором присваивания или мутации.
pub fn is_assignment_operator(ch: char) -> bool {
ch == OP_ASSIGN
}
/// Проверяет, является ли строка оператором мутации.
pub fn is_mutate_operator(s: &str) -> bool {
s == OP_MUTATE
}
/// Проверяет, является ли символ началом числового литерала.
pub fn is_digit_start(ch: char) -> bool {
ch.is_ascii_digit()
}
/// Проверяет, является ли символ разделителем разрядов.
pub fn is_digit_separator(ch: char) -> bool {
ch == DIGIT_SEPARATOR
}
/// Проверяет, является ли символ шестнадцатеричной цифрой.
pub fn is_hex_digit(ch: char) -> bool {
ch.is_ascii_hexdigit()
}
/// Проверяет, является ли символ двоичной цифрой.
pub fn is_binary_digit(ch: char) -> bool {
matches!(ch, '0' | '1')
}
/// Проверяет, является ли строка допустимым суффиксом типа.
pub fn is_type_suffix(s: &str) -> bool {
TYPE_SUFFIXES.contains(&s)
}
/// Проверяет, является ли символ началом оператора конвейера или записи.
pub fn is_pipeline_or_write_start(ch: char) -> bool {
matches!(ch, '|' | '<')
}
/// Проверяет, является ли символ оператором цикла.
pub fn is_loop_operator(ch: char) -> bool {
ch == LOOP_OP
}
#[cfg(test)]
mod tests {
use super::*;
// TODO: Написать тесты для функций грамматики
// - Тест is_identifier_prefix
// - Тест is_block_op_start
// - Тест is_comment_start_char
// - Тест is_comment_end_char
// - Тест is_identifier_char (кириллица, латиница, цифры)
}

34
src/lib.rs Normal file
View File

@@ -0,0 +1,34 @@
//! # Токенизатор языка Ъ+
//!
//! Доверенный лексический анализатор языка «Ъ+» (системный базис «Ъ++»).
//! Реализация на Rust (no_std) для архитектур «Эльбрус» и x86.
//!
//! ## Основные компоненты
//!
//! - Лексический анализ входных потоков данных
//! - Преобразование в последовательность токенов на основе ДКА
//! - Поддержка UTF-8 кодировки (ISO/IEC 10646)
//! - Обработка кириллических и латинских идентификаторов
//! - Отслеживание позиций токенов (строка, столбец)
#![no_std]
#![deny(missing_docs)]
// TODO: Настроить обработку ошибок для no_std окружения
// TODO: Добавить необходимые re-exports для core типов
pub mod comment;
pub mod dfa;
pub mod error;
pub mod grammar;
pub mod position;
pub mod token;
pub mod tokenizer;
pub mod utf8;
// Re-export основных типов для удобства использования
pub use error::{Error, ErrorKind};
pub use position::Position;
pub use token::{Token, TokenKind};
pub use tokenizer::Tokenizer;

113
src/position.rs Normal file
View File

@@ -0,0 +1,113 @@
//! Модуль отслеживания позиций токенов в исходном коде.
//!
//! Обеспечивает фиксацию координат (строка, столбец) каждого токена
//! для реализации сквозного аудита кода.
/// Позиция токена в исходном коде.
///
/// Хранит координаты (строка, столбец) для отслеживания местоположения
/// токена в исходном тексте.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Position {
/// Номер строки (начинается с 1)
pub line: u32,
/// Номер столбца в строке (начинается с 1)
pub column: u32,
}
impl Position {
/// Создает новую позицию в начале файла (строка 1, столбец 1).
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::Position;
/// let pos = Position::start();
/// assert_eq!(pos.line, 1);
/// assert_eq!(pos.column, 1);
/// ```
pub fn start() -> Self {
// TODO: Реализовать создание начальной позиции
Self { line: 1, column: 1 }
}
/// Создает позицию с указанными координатами.
///
/// # Параметры
/// - `line`: номер строки (должен быть >= 1)
/// - `column`: номер столбца (должен быть >= 1)
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::Position;
/// let pos = Position::new(5, 10);
/// ```
pub fn new(line: u32, column: u32) -> Self {
// TODO: Добавить проверку валидности координат
Self { line, column }
}
/// Перемещает позицию на один символ вправо (увеличивает столбец).
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::Position;
/// let mut pos = Position::start();
/// pos.advance_char();
/// assert_eq!(pos.column, 2);
/// ```
pub fn advance_char(&mut self) {
// TODO: Реализовать перемещение на один символ
self.column += 1;
}
/// Перемещение позиции на новую строку (увеличивает номер строки, сбрасывает столбец).
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::Position;
/// let mut pos = Position::new(1, 10);
/// pos.advance_line();
/// assert_eq!(pos.line, 2);
/// assert_eq!(pos.column, 1);
/// ```
pub fn advance_line(&mut self) {
// TODO: Реализовать перемещение на новую строку
self.line += 1;
self.column = 1;
}
/// Перемещение позиции на указанное количество символов.
///
/// # Параметры
/// - `count`: количество символов для перемещения
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::Position;
/// let mut pos = Position::start();
/// pos.advance_by(5);
/// assert_eq!(pos.column, 6);
/// ```
pub fn advance_by(&mut self, count: u32) {
// TODO: Реализовать перемещение на несколько символов
self.column += count;
}
}
impl Default for Position {
fn default() -> Self {
Self::start()
}
}
#[cfg(test)]
mod tests {
use super::*;
// TODO: Написать тесты для Position
// - Тест создания начальной позиции
// - Тест advance_char
// - Тест advance_line
// - Тест advance_by
}

296
src/token.rs Normal file
View File

@@ -0,0 +1,296 @@
//! Модуль определений типов токенов.
//!
//! Содержит определения типов `Token` и `TokenKind` для представления
//! результатов лексического анализа.
use crate::position::Position;
/// Тип токена языка Ъ+.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TokenKind {
// Идентификаторы с префиксами
/// Идентификатор переменной (префикс `@`)
VariableIdentifier(String),
/// Идентификатор метки управления (префикс `:`)
LabelIdentifier(String),
/// Оператор логического ветвления (префикс `?`)
PredicateIdentifier(String),
// Управляющие конструкции
/// Начало блока (`Ъ+`)
BlockStart,
/// Конец блока (`Ъ-`)
BlockEnd,
/// Оператор конвейера (`|>`)
PipelineOperator,
/// Оператор записи (`<<`)
WriteOperator,
/// Цикл (`>`)
LoopOperator,
// Арифметические операторы
/// Сложение (`+`)
Plus,
/// Вычитание (`-`)
Minus,
/// Умножение (`*`)
Multiply,
/// Деление (`/`)
Divide,
/// Остаток от деления (`%`)
Modulo,
/// Возведение в степень (`**`)
Power,
// Операторы сравнения
/// Равенство (`==`)
Equal,
/// Неравенство (`!=`)
NotEqual,
/// Меньше (`<`)
Less,
/// Больше (`>`)
Greater,
/// Меньше или равно (`<=`)
LessEqual,
/// Больше или равно (`>=`)
GreaterEqual,
// Логические операторы
/// Логическое И (`&&`)
LogicalAnd,
/// Логическое ИЛИ (`||`)
LogicalOr,
/// Логическое НЕ (`!`)
LogicalNot,
// Битовые операторы
/// Битовое И (`&`)
BitwiseAnd,
/// Битовое ИЛИ (`|`)
BitwiseOr,
/// Битовое исключающее ИЛИ (`^`)
BitwiseXor,
/// Битовое НЕ (`~`)
BitwiseNot,
/// Сдвиг влево (`<<`)
ShiftLeft,
/// Сдвиг вправо (`>>`)
ShiftRight,
// Операторы присваивания
/// Присваивание (`=`)
Assign,
/// Мутация (`:=`)
Mutate,
// Литералы
/// Целочисленный литерал
IntegerLiteral(u64),
/// Типизированный целочисленный литерал (значение, тип как строка)
TypedInteger(u64, String),
/// Вещественный литерал
FloatLiteral(f64),
/// Типизированный вещественный литерал (значение, тип как строка)
TypedFloat(f64, String),
/// Строковый литерал
StringLiteral(String),
/// Шестнадцатеричный литерал
HexLiteral(u64),
/// Двоичный литерал
BinaryLiteral(u64),
// Специальные символы
/// Двоеточие (`:`)
Colon,
/// Запятая (`,`)
Comma,
/// Точка с запятой (`;`)
Semicolon,
/// Левая круглая скобка (`(`)
LeftParen,
/// Правая круглая скобка (`)`)
RightParen,
/// Левая фигурная скобка (`{`)
LeftBrace,
/// Правая фигурная скобка (`}`)
RightBrace,
/// Левая квадратная скобка (`[`)
LeftBracket,
/// Правая квадратная скобка (`]`)
RightBracket,
/// Стрелка (`->`)
Arrow,
// Конструкции (для парсера)
/// Сопоставление с образцом (match)
Match,
/// Лямбда-функция
Lambda,
// Специальные токены
/// Конец файла
Eof,
/// Неизвестный токен (для обработки ошибок)
Unknown,
}
/// Токен языка Ъ+.
///
/// Представляет результат лексического анализа с информацией о типе токена,
/// его позиции в исходном коде и исходной строке (лексеме).
#[derive(Debug, Clone, PartialEq)]
pub struct Token {
/// Тип токена
pub kind: TokenKind,
/// Позиция начала токена в исходном коде
pub position: Position,
/// Исходная строка (лексема) токена
pub lexeme: String,
}
impl Token {
/// Создает новый токен.
///
/// # Параметры
/// - `kind`: тип токена
/// - `position`: позиция начала токена
/// - `lexeme`: исходная строка токена
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::token::{Token, TokenKind};
/// use tverd_plus_tokenizer::Position;
///
/// let token = Token::new(
/// TokenKind::BlockStart,
/// Position::start(),
/// "Ъ+".to_string()
/// );
/// ```
pub fn new(kind: TokenKind, position: Position, lexeme: String) -> Self {
// TODO: Реализовать создание токена
Self {
kind,
position,
lexeme,
}
}
/// Проверяет, является ли токен идентификатором.
///
/// # Возвращает
/// `true`, если токен является одним из типов идентификаторов
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::token::{Token, TokenKind};
/// use tverd_plus_tokenizer::Position;
///
/// let token = Token::new(
/// TokenKind::VariableIdentifier("test".to_string()),
/// Position::start(),
/// "@test".to_string()
/// );
/// assert!(token.is_identifier());
/// ```
pub fn is_identifier(&self) -> bool {
// TODO: Реализовать проверку идентификатора
matches!(
self.kind,
TokenKind::VariableIdentifier(_)
| TokenKind::LabelIdentifier(_)
| TokenKind::PredicateIdentifier(_)
)
}
/// Проверяет, является ли токен оператором.
///
/// # Возвращает
/// `true`, если токен является оператором
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::token::{Token, TokenKind};
/// use tverd_plus_tokenizer::Position;
///
/// let token = Token::new(
/// TokenKind::PipelineOperator,
/// Position::start(),
/// "|>".to_string()
/// );
/// assert!(token.is_operator());
/// ```
pub fn is_operator(&self) -> bool {
matches!(
self.kind,
TokenKind::PipelineOperator
| TokenKind::WriteOperator
| TokenKind::BlockStart
| TokenKind::BlockEnd
| TokenKind::LoopOperator
| TokenKind::Plus
| TokenKind::Minus
| TokenKind::Multiply
| TokenKind::Divide
| TokenKind::Modulo
| TokenKind::Power
| TokenKind::Equal
| TokenKind::NotEqual
| TokenKind::Less
| TokenKind::Greater
| TokenKind::LessEqual
| TokenKind::GreaterEqual
| TokenKind::LogicalAnd
| TokenKind::LogicalOr
| TokenKind::LogicalNot
| TokenKind::BitwiseAnd
| TokenKind::BitwiseOr
| TokenKind::BitwiseXor
| TokenKind::BitwiseNot
| TokenKind::ShiftLeft
| TokenKind::ShiftRight
| TokenKind::Assign
| TokenKind::Mutate
)
}
/// Получает текст идентификатора, если токен является идентификатором.
///
/// # Возвращает
/// `Some(&str)` с текстом идентификатора, или `None` если токен не идентификатор
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::token::{Token, TokenKind};
/// use tverd_plus_tokenizer::Position;
///
/// let token = Token::new(
/// TokenKind::VariableIdentifier("test".to_string()),
/// Position::start(),
/// "@test".to_string()
/// );
/// assert_eq!(token.identifier_text(), Some("test"));
/// ```
pub fn identifier_text(&self) -> Option<&str> {
// TODO: Реализовать получение текста идентификатора
match &self.kind {
TokenKind::VariableIdentifier(s)
| TokenKind::LabelIdentifier(s)
| TokenKind::PredicateIdentifier(s) => Some(s),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
// TODO: Написать тесты для Token
// - Тест создания токена
// - Тест is_identifier
// - Тест is_operator
// - Тест identifier_text
}

215
src/tokenizer.rs Normal file
View File

@@ -0,0 +1,215 @@
//! Модуль основного лексического анализатора (токенизатора).
//!
//! Реализует однопроходный сканер на основе детерминированного конечного автомата (ДКА)
//! для преобразования входного потока данных в последовательность токенов.
use crate::comment::CommentState;
use crate::dfa::DfaState;
use crate::error::{Error, ErrorKind};
use crate::grammar;
use crate::position::Position;
use crate::token::{Token, TokenKind};
use crate::utf8;
/// Лексический анализатор языка Ъ+.
///
/// Выполняет однопроходное сканирование входного потока и преобразует его
/// в последовательность токенов с отслеживанием позиций.
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::Tokenizer;
///
/// let source = "Ъ+ @переменная :метка |>";
/// let mut tokenizer = Tokenizer::new(source);
/// // Использование итератора для получения токенов
/// ```
pub struct Tokenizer<'a> {
/// Исходный текст для анализа
source: &'a str,
/// Текущая позиция в исходном тексте (в байтах)
position: usize,
/// Текущая позиция в исходном коде (строка, столбец)
current_position: Position,
/// Состояние обработки комментариев
comment_state: CommentState,
/// Текущее состояние ДКА
state: DfaState,
}
impl<'a> Tokenizer<'a> {
/// Создает новый токенизатор для указанного исходного текста.
///
/// # Параметры
/// - `source`: исходный текст для лексического анализа
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::Tokenizer;
///
/// let source = "Ъ+ @переменная";
/// let tokenizer = Tokenizer::new(source);
/// ```
pub fn new(source: &'a str) -> Self {
// TODO: Реализовать создание токенизатора
Self {
source,
position: 0,
current_position: Position::start(),
comment_state: CommentState::new(),
state: DfaState::Initial,
}
}
/// Читает следующий символ из исходного текста без перемещения позиции.
///
/// # Возвращает
/// `Option<char>` - следующий символ, или `None` если достигнут конец файла
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::Tokenizer;
///
/// let source = "Ъ+";
/// let tokenizer = Tokenizer::new(source);
/// // peek_char() используется внутренне
/// ```
fn peek_char(&self) -> Option<char> {
// TODO: Реализовать просмотр следующего символа
self.source[self.position..].chars().next()
}
/// Читает следующий символ из исходного текста с перемещением позиции.
///
/// # Возвращает
/// `Option<char>` - следующий символ, или `None` если достигнут конец файла
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::Tokenizer;
///
/// let source = "Ъ+";
/// let mut tokenizer = Tokenizer::new(source);
/// // next_char() используется внутренне
/// ```
fn next_char(&mut self) -> Option<char> {
// TODO: Реализовать чтение следующего символа с обновлением позиции
let mut chars = self.source[self.position..].char_indices();
if let Some((_, ch)) = chars.next() {
let char_len = ch.len_utf8();
self.position += char_len;
// Обновляем позицию (строка, столбец)
if ch == '\n' {
self.current_position.advance_line();
} else {
self.current_position.advance_char();
}
Some(ch)
} else {
None
}
}
/// Пропускает пробельные символы и символы новой строки.
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::Tokenizer;
///
/// let source = " \n @переменная";
/// let mut tokenizer = Tokenizer::new(source);
/// // skip_whitespace() используется внутренне
/// ```
fn skip_whitespace(&mut self) {
// TODO: Реализовать пропуск пробельных символов
while let Some(ch) = self.peek_char() {
if ch.is_whitespace() {
self.next_char();
} else {
break;
}
}
}
/// Распознает следующий токен из исходного текста.
///
/// # Возвращает
/// `Result<Option<Token>, Error>` - следующий токен или ошибка
/// - `Ok(Some(token))` - успешно распознан токен
/// - `Ok(None)` - достигнут конец файла
/// - `Err(error)` - произошла ошибка лексического анализа
///
/// # Пример
/// ```
/// use tverd_plus_tokenizer::Tokenizer;
///
/// let source = "Ъ+";
/// let mut tokenizer = Tokenizer::new(source);
/// match tokenizer.next_token() {
/// Ok(Some(token)) => println!("Токен: {:?}", token),
/// Ok(None) => println!("Конец файла"),
/// Err(e) => println!("Ошибка: {:?}", e),
/// }
/// ```
pub fn next_token(&mut self) -> Result<Option<Token>, Error> {
// TODO: Реализовать распознавание следующего токена
// Алгоритм:
// 1. Пропустить пробельные символы
// 2. Проверить, достигнут ли конец файла
// 3. Проверить, находимся ли внутри комментария
// 4. Использовать ДКА для распознавания токена
// 5. Вернуть распознанный токен или ошибку
self.skip_whitespace();
if self.position >= self.source.len() {
return Ok(Some(Token::new(
TokenKind::Eof,
self.current_position,
String::new(),
)));
}
// Заглушка - возвращает ошибку
Err(Error::new(
ErrorKind::Other("Не реализовано"),
self.current_position,
))
}
}
impl<'a> Iterator for Tokenizer<'a> {
type Item = Result<Token, Error>;
fn next(&mut self) -> Option<Self::Item> {
// TODO: Реализовать итератор токенов
match self.next_token() {
Ok(Some(token)) => {
if matches!(token.kind, TokenKind::Eof) {
None
} else {
Some(Ok(token))
}
}
Ok(None) => None,
Err(e) => Some(Err(e)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
// TODO: Написать тесты для Tokenizer
// - Тест создания токенизатора
// - Тест next_token для различных типов токенов
// - Тест обработки комментариев
// - Тест обработки идентификаторов (кириллица, латиница)
// - Тест обработки операторов
// - Тест отслеживания позиций
// - Тест итератора
}

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
}

View File

@@ -0,0 +1,25 @@
//! Интеграционные тесты для токенизатора языка Ъ+.
// TODO: Добавить интеграционные тесты
// - Тест полного цикла токенизации простого примера
// - Тест обработки различных типов токенов в одном файле
// - Тест обработки больших файлов
// - Тест производительности токенизации
#[cfg(test)]
mod tests {
// use tverd_plus_tokenizer::*;
#[test]
fn test_integration_basic() {
// TODO: Реализовать базовый интеграционный тест
// Пример: токенизация простого кода с несколькими типами токенов
}
#[test]
fn test_integration_complex() {
// TODO: Реализовать сложный интеграционный тест
// Пример: токенизация кода с вложенными комментариями и различными идентификаторами
}
}

51
tests/test_comments.rs Normal file
View File

@@ -0,0 +1,51 @@
//! Тесты для вложенных комментариев.
// TODO: Добавить тесты для комментариев
// - Тест простого комментария (<[ комментарий ]>)
// - Тест вложенных комментариев (<[ внешний <[ внутренний ]> внешний ]>)
// - Тест многоуровневой вложенности
// - Тест комментариев с различным содержимым
// - Тест ошибок несоответствия комментариев
// - Тест комментариев в различных позициях кода
#[cfg(test)]
mod tests {
// use tverd_plus_tokenizer::*;
#[test]
fn test_simple_comment() {
// TODO: Тест простого комментария
// Пример: "<[ комментарий ]>"
}
#[test]
fn test_nested_comments() {
// TODO: Тест вложенных комментариев
// Пример: "<[ внешний <[ внутренний ]> внешний ]>"
}
#[test]
fn test_multiple_nesting_levels() {
// TODO: Тест многоуровневой вложенности
// Пример: "<[ 1 <[ 2 <[ 3 ]> 2 ]> 1 ]>"
}
#[test]
fn test_comments_with_content() {
// TODO: Тест комментариев с различным содержимым
// Пример: "<[ комментарий с @символами и операторами |> ]>"
}
#[test]
fn test_unmatched_comment_close() {
// TODO: Тест ошибки несоответствия комментариев
// Пример: "]> без открытия"
}
#[test]
fn test_comments_in_code() {
// TODO: Тест комментариев в различных позициях кода
// Пример: "Ъ+ <[ комментарий ]> @переменная"
}
}

50
tests/test_cyrillic.rs Normal file
View File

@@ -0,0 +1,50 @@
//! Тесты для верификации кириллических идентификаторов.
// TODO: Добавить тесты для кириллических идентификаторов
// - Тест идентификаторов переменных с кириллицей (@переменная)
// - Тест идентификаторов меток с кириллицей (:метка)
// - Тест предикатов с кириллицей (?предикат)
// - Тест смешанных идентификаторов (кириллица + латиница)
// - Тест различных регистров кириллицы (заглавные, строчные, ё/Ё)
#[cfg(test)]
mod tests {
// use tverd_plus_tokenizer::*;
#[test]
fn test_cyrillic_variable_identifier() {
// TODO: Тест идентификатора переменной с кириллицей
// Пример: "@переменная"
}
#[test]
fn test_cyrillic_label_identifier() {
// TODO: Тест идентификатора метки с кириллицей
// Пример: ":метка"
}
#[test]
fn test_cyrillic_predicate_identifier() {
// TODO: Тест предиката с кириллицей
// Пример: "?предикат"
}
#[test]
fn test_mixed_cyrillic_latin() {
// TODO: Тест смешанных идентификаторов
// Пример: "@переменнаяVar"
}
#[test]
fn test_cyrillic_case_sensitivity() {
// TODO: Тест различных регистров кириллицы
// Пример: "@Переменная", "@ПЕРЕМЕННАЯ", "@переменная"
}
#[test]
fn test_cyrillic_yo_letter() {
// TODO: Тест буквы ё/Ё
// Пример: "@пёс", "@ПЁС"
}
}

50
tests/test_latin.rs Normal file
View File

@@ -0,0 +1,50 @@
//! Тесты для верификации латинских идентификаторов.
// TODO: Добавить тесты для латинских идентификаторов
// - Тест идентификаторов переменных с латиницей (@variable)
// - Тест идентификаторов меток с латиницей (:label)
// - Тест предикатов с латиницей (?predicate)
// - Тест смешанного регистра (camelCase, snake_case)
// - Тест цифр в идентификаторах (после первого символа)
#[cfg(test)]
mod tests {
// use tverd_plus_tokenizer::*;
#[test]
fn test_latin_variable_identifier() {
// TODO: Тест идентификатора переменной с латиницей
// Пример: "@variable"
}
#[test]
fn test_latin_label_identifier() {
// TODO: Тест идентификатора метки с латиницей
// Пример: ":label"
}
#[test]
fn test_latin_predicate_identifier() {
// TODO: Тест предиката с латиницей
// Пример: "?predicate"
}
#[test]
fn test_camel_case() {
// TODO: Тест camelCase идентификаторов
// Пример: "@camelCase"
}
#[test]
fn test_snake_case() {
// TODO: Тест snake_case идентификаторов
// Пример: "@snake_case"
}
#[test]
fn test_identifier_with_digits() {
// TODO: Тест идентификаторов с цифрами
// Пример: "@var123", "@var1_2"
}
}

51
tests/test_operators.rs Normal file
View File

@@ -0,0 +1,51 @@
//! Тесты для операторов языка Ъ+.
// TODO: Добавить тесты для операторов
// - Тест оператора начала блока (Ъ+)
// - Тест оператора конца блока (Ъ-)
// - Тест оператора конвейера (|>)
// - Тест оператора записи (<<)
// - Тест позиций операторов
// - Тест комбинаций операторов
#[cfg(test)]
mod tests {
// use tverd_plus_tokenizer::*;
#[test]
fn test_block_start_operator() {
// TODO: Тест оператора начала блока
// Пример: "Ъ+"
}
#[test]
fn test_block_end_operator() {
// TODO: Тест оператора конца блока
// Пример: "Ъ-"
}
#[test]
fn test_pipeline_operator() {
// TODO: Тест оператора конвейера
// Пример: "|>"
}
#[test]
fn test_write_operator() {
// TODO: Тест оператора записи
// Пример: "<<"
}
#[test]
fn test_operator_combinations() {
// TODO: Тест комбинаций операторов
// Пример: "Ъ+ @var |> <<"
}
#[test]
fn test_operator_positions() {
// TODO: Тест отслеживания позиций операторов
// Проверить, что позиции операторов корректно отслеживаются
}
}

51
tests/test_positions.rs Normal file
View File

@@ -0,0 +1,51 @@
//! Тесты для отслеживания позиций токенов.
// TODO: Добавить тесты для отслеживания позиций
// - Тест позиции первого токена
// - Тест позиции после пробелов
// - Тест позиции после новой строки
// - Тест позиции в многострочном коде
// - Тест позиции в комментариях
// - Тест позиции ошибок
#[cfg(test)]
mod tests {
// use tverd_plus_tokenizer::*;
#[test]
fn test_first_token_position() {
// TODO: Тест позиции первого токена
// Проверить, что первый токен имеет позицию (1, 1)
}
#[test]
fn test_position_after_whitespace() {
// TODO: Тест позиции после пробелов
// Пример: " @var" - проверить позицию @var
}
#[test]
fn test_position_after_newline() {
// TODO: Тест позиции после новой строки
// Пример: "\n@var" - проверить, что @var на строке 2
}
#[test]
fn test_multiline_positions() {
// TODO: Тест позиций в многострочном коде
// Пример: "Ъ+\n @var\n :метка"
}
#[test]
fn test_comment_positions() {
// TODO: Тест позиций комментариев
// Проверить, что комментарии имеют корректные позиции
}
#[test]
fn test_error_positions() {
// TODO: Тест позиций ошибок
// Проверить, что ошибки содержат корректные позиции
}
}