feat: базовая структура проекта; docs: грамматика формы бэкуса-наура
This commit is contained in:
24
.gitignore
vendored
24
.gitignore
vendored
@@ -1,14 +1,16 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# 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
|
||||
# Rust
|
||||
/target/
|
||||
**/*.rs.bk
|
||||
*.pdb
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
38
Cargo.toml
Normal file
38
Cargo.toml
Normal 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
|
||||
|
||||
34
benches/tokenizer_bench.rs
Normal file
34
benches/tokenizer_bench.rs
Normal 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
262
docs/grammar.md
Normal 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
|
||||
63
examples/basic_tokenizer.rs
Normal file
63
examples/basic_tokenizer.rs
Normal 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
176
src/comment.rs
Normal 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
181
src/dfa.rs
Normal 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
124
src/error.rs
Normal 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
262
src/grammar.rs
Normal 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
34
src/lib.rs
Normal 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
113
src/position.rs
Normal 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
296
src/token.rs
Normal 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
215
src/tokenizer.rs
Normal 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
202
src/utf8.rs
Normal 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
|
||||
}
|
||||
|
||||
25
tests/integration_tests.rs
Normal file
25
tests/integration_tests.rs
Normal 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
51
tests/test_comments.rs
Normal 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
50
tests/test_cyrillic.rs
Normal 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
50
tests/test_latin.rs
Normal 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
51
tests/test_operators.rs
Normal 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
51
tests/test_positions.rs
Normal 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: Тест позиций ошибок
|
||||
// Проверить, что ошибки содержат корректные позиции
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user