feat: базовая структура проекта; docs: грамматика формы бэкуса-наура
This commit is contained in:
24
.gitignore
vendored
24
.gitignore
vendored
@@ -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
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