244 lines
12 KiB
Dart
244 lines
12 KiB
Dart
/// Канонический реестр культурных ветвей человечества, через которые
|
||
/// толкуется каждый знак. Порядок списка — порядок показа в «учебнике».
|
||
class CultureBranch {
|
||
final String id;
|
||
final String name;
|
||
final String emoji;
|
||
final String subtitle;
|
||
|
||
/// Стемы/ключевые слова (в нижнем регистре), по которым ветвь узнаётся в тексте.
|
||
final List<String> stems;
|
||
|
||
const CultureBranch({
|
||
required this.id,
|
||
required this.name,
|
||
required this.emoji,
|
||
required this.subtitle,
|
||
required this.stems,
|
||
});
|
||
|
||
/// Dart-овский `\b` не понимает кириллицу, поэтому граница слова
|
||
/// задаётся явным lookbehind по буквам.
|
||
RegExp get matcher => RegExp(
|
||
'(?<![a-zа-яё])(?:${stems.map(RegExp.escape).join('|')})',
|
||
caseSensitive: false,
|
||
unicode: true,
|
||
);
|
||
|
||
bool matches(String text) => matcher.hasMatch(text.toLowerCase());
|
||
}
|
||
|
||
const List<CultureBranch> kCultureBranches = [
|
||
CultureBranch(
|
||
id: 'sumer',
|
||
name: 'Шумер и Месопотамия',
|
||
emoji: '🏺',
|
||
subtitle: 'Клинопись • Ану, Энлиль, Инанна, Уту',
|
||
stems: ['шумер', 'аккад', 'месопотам', 'вавилон', 'ассири', 'клинопис', 'уту', 'инанн', 'иштар', 'энлил', 'энки', 'мардук', 'тиамат', 'ануннак', 'гильгамеш', 'эрешкигаль', 'mūšu', 'ilū'],
|
||
),
|
||
CultureBranch(
|
||
id: 'egypt',
|
||
name: 'Древний Египет',
|
||
emoji: '🪲',
|
||
subtitle: 'Нил и Дуат • Ра, Осирис, Исида, Маат',
|
||
stems: ['египет', 'египт', 'фараон', 'апоп', 'нут ', 'нут,', 'дуат', 'осирис', 'исид', 'тот ', 'маат', 'анубис', 'сехмет', 'хепри', 'атум', 'птах', 'ра ', 'ра,', 'ра.', 'гор '],
|
||
),
|
||
CultureBranch(
|
||
id: 'judaic',
|
||
name: 'Иудейская традиция и Каббала',
|
||
emoji: '✡️',
|
||
subtitle: 'Танах, Талмуд, Зоар • Древо Сфирот',
|
||
stems: ['иудей', 'иудаизм', 'еврей', 'каббал', 'танах', 'тора ', 'торы', 'талмуд', 'зоар', 'библ', 'бытие', 'бытия', 'сфир', 'цимцум', 'иврит', 'йом', 'эрев', 'лайла', 'хошех', 'эйн-соф', 'эйн соф', 'шхин'],
|
||
),
|
||
CultureBranch(
|
||
id: 'greco_roman',
|
||
name: 'Античность (Греция и Рим)',
|
||
emoji: '🏛️',
|
||
subtitle: 'Гесиод, Гомер, Платон • Олимп и Хаос',
|
||
stems: ['греци', 'греческ', 'грек', 'эллин', 'римск', 'рима', 'латин', 'античн', 'зевс', 'нюкт', 'гемер', 'эфир', 'юпитер', 'аполлон', 'гелиос', 'гомер', 'гесиод', 'платон', 'персефон', 'гермес', 'dies irae'],
|
||
),
|
||
CultureBranch(
|
||
id: 'celtic',
|
||
name: 'Кельты и друиды',
|
||
emoji: '🌿',
|
||
subtitle: 'Огам, Самайн, Белтайн • Иной мир сидов',
|
||
stems: ['кельт', 'огам', 'друид', 'самайн', 'белтайн', 'имболк', 'лугнас', 'ирланд', 'валлий', 'бригит', 'сиды', 'туата'],
|
||
),
|
||
CultureBranch(
|
||
id: 'norse',
|
||
name: 'Скандинавия и германцы',
|
||
emoji: 'ᚱ',
|
||
subtitle: 'Эдды, руны • Один, Иггдрасиль, Рагнарёк',
|
||
stems: ['скандинав', 'германск', 'норвеж', 'исланд', 'эдд', 'руна ', 'руны', 'рун ', 'одина', 'одину', 'иггдрасил', 'рагнар', 'асгард', 'вальхал', 'фрейя', 'локи', 'норны', 'дагаз'],
|
||
),
|
||
CultureBranch(
|
||
id: 'slavic',
|
||
name: 'Славянская традиция',
|
||
emoji: '☀️',
|
||
subtitle: 'Явь, Навь, Правь • Велес, Перун, Макошь',
|
||
stems: ['славян', 'древнерус', 'явь', 'навь', 'нави ', 'правь', 'велес', 'перун', 'макош', 'мокош', 'сварог', 'даждьбог', 'ярил', 'купал', 'коляд', 'стрибог', 'домов', 'леший', 'русалк'],
|
||
),
|
||
CultureBranch(
|
||
id: 'vedic',
|
||
name: 'Ведическая Индия',
|
||
emoji: '🕉️',
|
||
subtitle: 'Веды и Упанишады • Рита, карма, атман',
|
||
stems: ['индийск', 'индуи', 'ведическ', 'веды', 'вед ', 'ригвед', 'упанишад', 'санскрит', 'дхарм', 'карм', 'брахман', 'атман', 'сурья', 'агни', 'индра', 'шива', 'вишну', 'кали ', 'рита ', 'тантр'],
|
||
),
|
||
CultureBranch(
|
||
id: 'persian',
|
||
name: 'Персия и зороастризм',
|
||
emoji: '🔥',
|
||
subtitle: 'Авеста • Ахура-Мазда и Ахриман, Аша',
|
||
stems: ['персид', 'персы', 'иран', 'зороастр', 'заратустр', 'авест', 'ахура', 'ахриман', 'митр'],
|
||
),
|
||
CultureBranch(
|
||
id: 'china',
|
||
name: 'Древний Китай',
|
||
emoji: '☯️',
|
||
subtitle: 'Дао, Инь-Ян, У-син • Книга Перемен',
|
||
stems: ['китай', 'дао', 'инь', 'ян ', 'ян,', 'ян.', 'ян:', 'и цзин', 'и-цзин', 'гексаграмм', 'триграмм', 'конфуци', 'лао-цзы', 'лао цзы', 'чжуан', 'у-син', 'у син', 'иероглиф'],
|
||
),
|
||
CultureBranch(
|
||
id: 'japan',
|
||
name: 'Япония и синто',
|
||
emoji: '⛩️',
|
||
subtitle: 'Кодзики • Аматэрасу, ками, дзэн',
|
||
stems: ['япон', 'синто', 'аматэрасу', 'ками ', 'кодзики', 'дзэн', 'идзанаги', 'идзанами'],
|
||
),
|
||
CultureBranch(
|
||
id: 'islamic',
|
||
name: 'Ислам и суфизм',
|
||
emoji: '🌙',
|
||
subtitle: 'Коран, суфийская поэзия • Руми, Ибн Араби',
|
||
stems: ['ислам', 'суфи', 'суфий', 'арабск', 'коран', 'лайл ', 'лайл,', 'мирадж', 'лайлят', 'тасаввуф', 'руми', 'дервиш', 'ибн араби', 'геомант', 'рамл', 'тетраграмм'],
|
||
),
|
||
CultureBranch(
|
||
id: 'christian',
|
||
name: 'Христианская мистика',
|
||
emoji: '✝️',
|
||
subtitle: 'Отцы-пустынники, Апокалипсис • Логос',
|
||
stems: ['христиан', 'евангел', 'апокалипс', 'отшельник', 'пустынник', 'монах', 'литург', 'daemon meridianus', 'акеди', 'страшный суд', 'иисус'],
|
||
),
|
||
CultureBranch(
|
||
id: 'mesoamerica',
|
||
name: 'Мезоамерика и Анды',
|
||
emoji: '🌞',
|
||
subtitle: 'Ацтеки, майя, инки • Кецалькоатль, Тонатиу',
|
||
stems: ['ацтек', 'майя', 'мезоамерик', 'тонатиу', 'кецалькоатл', 'тескатлипок', 'попол-вух', 'попол вух', 'инки', 'инков'],
|
||
),
|
||
CultureBranch(
|
||
id: 'oceania',
|
||
name: 'Полинезия и Океания',
|
||
emoji: '🌺',
|
||
subtitle: 'По и Ао • Ранги и Папа, мана, табу',
|
||
stems: ['полинези', 'маори', 'гавай', 'pō', 'ao)', 'ранги', 'тангароа'],
|
||
),
|
||
CultureBranch(
|
||
id: 'hermetic',
|
||
name: 'Алхимия и герметизм',
|
||
emoji: '⚗️',
|
||
subtitle: 'Изумрудная скрижаль • Нигредо → Рубедо',
|
||
stems: ['алхими', 'гермет', 'нигредо', 'альбедо', 'цитринитас', 'рубедо', 'атанор', 'трисмегист', 'великое делание', 'философский камень', 'геоманти', 'геомант', 'puer', 'rubeus', 'albus'],
|
||
),
|
||
];
|
||
|
||
CultureBranch? cultureBranchByName(String name) {
|
||
final lower = name.toLowerCase().trim();
|
||
for (final b in kCultureBranches) {
|
||
if (b.name.toLowerCase() == lower || b.id == lower) return b;
|
||
}
|
||
for (final b in kCultureBranches) {
|
||
if (b.matches(' $lower ')) return b;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/// Раскладывает текст толкования на культурные ветви: каждый абзац,
|
||
/// где ветвь упомянута, попадает в её раздел (абзац может принадлежать
|
||
/// нескольким культурам — это и есть материал для сравнения).
|
||
Map<String, String> extractCulturalBranches(String text) {
|
||
final result = <String, String>{};
|
||
if (text.trim().isEmpty) return result;
|
||
final paragraphs = text
|
||
.split(RegExp(r'\r?\n'))
|
||
.map((p) => p.trim())
|
||
.where((p) => p.isNotEmpty)
|
||
.toList();
|
||
for (final branch in kCultureBranches) {
|
||
final hits = paragraphs.where((p) => branch.matches(' $p ')).toList();
|
||
if (hits.isNotEmpty) result[branch.name] = hits.join('\n\n');
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/// Сортирует произвольную карту культур в каноническом порядке реестра.
|
||
List<MapEntry<String, String>> orderedCultures(Map<String, String> cultures) {
|
||
int rank(String name) {
|
||
final b = cultureBranchByName(name);
|
||
return b == null ? 999 : kCultureBranches.indexOf(b);
|
||
}
|
||
|
||
final entries = cultures.entries.where((e) => e.value.trim().isNotEmpty).toList()
|
||
..sort((a, b) => rank(a.key).compareTo(rank(b.key)));
|
||
return entries;
|
||
}
|
||
|
||
/// Разбирает блок «## КУЛЬТУРЫ» на ветви по подзаголовкам
|
||
/// (`### Шумер и Месопотамия`, `**Древний Египет**`, `Кельты:` …).
|
||
Map<String, String> parseCulturesBlock(String block) {
|
||
final result = <String, String>{};
|
||
final lines = block.replaceAll('\r\n', '\n').split('\n');
|
||
String? current;
|
||
final buf = StringBuffer();
|
||
|
||
void flush() {
|
||
final text = buf.toString().trim();
|
||
final key = current;
|
||
if (key != null && text.isNotEmpty) {
|
||
result[key] = result.containsKey(key) ? '${result[key]}\n\n$text' : text;
|
||
}
|
||
buf.clear();
|
||
}
|
||
|
||
String? headingOf(String raw) {
|
||
final line = raw.trim();
|
||
if (line.isEmpty || line.length > 90) return null;
|
||
final looksLikeHeading = line.startsWith('#') ||
|
||
(line.startsWith('**') && line.endsWith('**')) ||
|
||
RegExp(r'^[-•*]?\s*\*\*[^*]+\*\*\s*[:—–-]?\s*$').hasMatch(line) ||
|
||
(line.endsWith(':') && line.length < 60);
|
||
if (!looksLikeHeading) return null;
|
||
final clean = line
|
||
.replaceAll(RegExp(r'^[#\s•*-]+'), '')
|
||
.replaceAll(RegExp(r'[*:—–\s-]+$'), '')
|
||
.replaceAll('*', '')
|
||
.trim();
|
||
final branch = cultureBranchByName(clean);
|
||
return branch?.name ?? (clean.isNotEmpty ? clean : null);
|
||
}
|
||
|
||
for (final line in lines) {
|
||
final h = headingOf(line);
|
||
if (h != null) {
|
||
flush();
|
||
current = h;
|
||
} else {
|
||
buf.writeln(line);
|
||
}
|
||
}
|
||
flush();
|
||
return result;
|
||
}
|
||
|
||
/// Обратная операция для редактора: карта культур → markdown-блок.
|
||
String formatCulturesBlock(Map<String, String> cultures) {
|
||
final sb = StringBuffer();
|
||
for (final e in orderedCultures(cultures)) {
|
||
sb.writeln('### ${e.key}');
|
||
sb.writeln(e.value.trim());
|
||
sb.writeln();
|
||
}
|
||
return sb.toString().trim();
|
||
}
|