feat: complete academic symbol lexicon heal, 2-column Cooper extraction, and updated assets
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
test('Markdown parser extracts all 5 blocks without loss or substring collisions', () {
|
||||
const input = '''```markdown
|
||||
## 1. ЯДРО / ФОРМУЛА ЗАКОНА:
|
||||
(1-2 чеканных предложения)
|
||||
Огонь — это первородная сингулярность трансформации материи в чистую радиацию духа.
|
||||
|
||||
## 2. ОРАКУЛ (МОНОЛИТНОЕ ЭССЕ):
|
||||
(5-8 абзацев)
|
||||
Праиндоевропейский корень *egni- фиксирует не субстанцию, а событие горения.
|
||||
В шумерском мифе бог Гибил нисходит в тигель трансформации.
|
||||
В алхимии огонь выступает универсальным растворителем формы ради освобождения скрытой субстанции.
|
||||
|
||||
## 3. ТЕНЕВАЯ ЛОВУШКА И ИСКАЖЕНИЕ:
|
||||
(Теневая ловушка принципа)
|
||||
Теневая ловушка принципа — испепеление контекста, мания тотального контроля через разрушение.
|
||||
|
||||
## 4. ЭКЗИСТЕНЦИАЛЬНЫЙ ВОПРОС:
|
||||
(Острый коан)
|
||||
Что именно в твоей структуре ты отказываешься отдать пламени обновления?
|
||||
|
||||
## 5. РИТУАЛЬНЫЙ ФИЗИЧЕСКИЙ ЖЕСТ:
|
||||
(Конкретное микродействие)
|
||||
Взять сухую щепку, разломить её надвое и бросить в открытый огонь, не оборачиваясь.
|
||||
|
||||
Метрика: 4120 символов
|
||||
```''';
|
||||
|
||||
Map<String, String> parseRevisionBlocks(String text) {
|
||||
final blocks = <String, String>{};
|
||||
if (text.trim().isEmpty) return blocks;
|
||||
|
||||
var src = text.trim();
|
||||
if (src.startsWith('```')) {
|
||||
src = src.replaceFirst(RegExp(r'^```[a-zA-Z]*\n?'), '');
|
||||
if (src.endsWith('```')) {
|
||||
src = src.substring(0, src.length - 3).trim();
|
||||
}
|
||||
}
|
||||
|
||||
final lines = src.replaceAll('\r\n', '\n').split('\n');
|
||||
|
||||
String? detectSlot(String line) {
|
||||
final stripped = line.trim();
|
||||
if (stripped.isEmpty || stripped.length > 95) return null;
|
||||
|
||||
final isHeaderFormat = stripped.startsWith('#') ||
|
||||
stripped.startsWith('*') ||
|
||||
stripped.startsWith('[') ||
|
||||
RegExp(r'^(?:\d+[\.\)]|[IVX]+[\.\)])').hasMatch(stripped) ||
|
||||
(stripped.length < 70 && (stripped.endsWith(':') || stripped == stripped.toUpperCase())) ||
|
||||
RegExp(r'\b(?:ЯДРО|ОРАКУЛ|ТЕНЬ|ВОПРОС|ЖЕСТ|КОАН)\b', caseSensitive: false).hasMatch(stripped);
|
||||
|
||||
if (!isHeaderFormat) return null;
|
||||
|
||||
if (RegExp(r'(?:^|[^\wа-яА-ЯёЁ])(?:ЯДР[ОАЕУ]|ФОРМУЛ[А-Яа-я]*|ОНТОЛОГИ[А-Яа-я]*)(?:[^\wа-яА-ЯёЁ]|$)', caseSensitive: false).hasMatch(stripped)) {
|
||||
return 'ЯДРО';
|
||||
}
|
||||
if (RegExp(r'(?:^|[^\wа-яА-ЯёЁ])(?:ВОПРОС[А-Яа-я]*|КОАН[А-Яа-я]*|РЕФЛЕКСИ[А-Яа-я]*)(?:[^\wа-яА-ЯёЁ]|$)', caseSensitive: false).hasMatch(stripped)) {
|
||||
return 'ВОПРОС';
|
||||
}
|
||||
if (RegExp(r'(?:^|[^\wа-яА-ЯёЁ])(?:ТЕН[ЬЯЕИЮ]|ТЕНЕВ[А-Яа-я]*|ЛОВУШК[А-Яа-я]*|ИСКАЖЕН[А-Яа-я]*|СЛЕП[А-Яа-я]*\s+ЗОН[А-Яа-я]*)(?:[^\wа-яА-ЯёЁ]|$)', caseSensitive: false).hasMatch(stripped)) {
|
||||
return 'ТЕНЬ';
|
||||
}
|
||||
if (RegExp(r'(?:^|[^\wа-яА-ЯёЁ])(?:ЖЕСТ[А-Яа-я]*|МИКРОДЕЙСТВИ[А-Яа-я]*|ДЕЙСТВИ[ЕЯ]|ПРАКТИК[А-Яа-я]*)(?:[^\wа-яА-ЯёЁ]|$)', caseSensitive: false).hasMatch(stripped)) {
|
||||
return 'ЖЕСТ';
|
||||
}
|
||||
if (RegExp(r'(?:^|[^\wа-яА-ЯёЁ])(?:ОРАКУЛ[А-Яа-я]*|СТАТЬ[ЯЕИЮ]|ЭССЕ|ТОЛКОВАНИ[А-Яа-я]*|ОБОГАЩЕНИ[А-Яа-я]*)(?:[^\wа-яА-ЯёЁ]|$)', caseSensitive: false).hasMatch(stripped)) {
|
||||
return 'ОРАКУЛ';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
final headers = <({int lineIdx, String slot})>[];
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
final slot = detectSlot(lines[i]);
|
||||
if (slot != null) {
|
||||
headers.add((lineIdx: i, slot: slot));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
final startLineIdx = headers[i].lineIdx + 1;
|
||||
final slot = headers[i].slot;
|
||||
final endLineIdx = (i + 1 < headers.length) ? headers[i + 1].lineIdx : lines.length;
|
||||
|
||||
var content = lines.sublist(startLineIdx, endLineIdx).join('\n').trim();
|
||||
|
||||
content = content.replaceFirst(
|
||||
RegExp(r'^\s*\([^)]*(?:предложен|абзац|коан|действи|формул|ловушк|стать)[^)]*\)\s*\n?', caseSensitive: false),
|
||||
'',
|
||||
).trim();
|
||||
|
||||
content = content.replaceAll(
|
||||
RegExp(r'(?:\n|\r\n)\s*(?:Метрика|Количество символов|Символов|Длина текста)[:—–\s][\s\S]*$', caseSensitive: false),
|
||||
'',
|
||||
).trim();
|
||||
|
||||
if (content.isNotEmpty) {
|
||||
if (!blocks.containsKey(slot) || content.length > blocks[slot]!.length) {
|
||||
blocks[slot] = content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
final result = parseRevisionBlocks(input);
|
||||
expect(result.length, 5);
|
||||
expect(result.containsKey('ЯДРО'), true);
|
||||
expect(result.containsKey('ОРАКУЛ'), true);
|
||||
expect(result.containsKey('ТЕНЬ'), true);
|
||||
expect(result.containsKey('ВОПРОС'), true);
|
||||
expect(result.containsKey('ЖЕСТ'), true);
|
||||
|
||||
expect(result['ЯДРО']!.startsWith('Огонь — это первородная'), true);
|
||||
expect(result['ОРАКУЛ']!.contains('Праиндоевропейский корень'), true);
|
||||
expect(result['ТЕНЬ']!.contains('Теневая ловушка принципа'), true);
|
||||
expect(result['ВОПРОС']!.contains('Что именно в твоей структуре'), true);
|
||||
expect(result['ЖЕСТ']!.contains('Взять сухую щепку'), true);
|
||||
expect(result['ЖЕСТ']!.contains('Метрика'), false);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user