v0.5.5
Update Promt Updata to-do
This commit is contained in:
@@ -0,0 +1,459 @@
|
||||
# Диалект Markdown md2gost — полная спецификация
|
||||
|
||||
Справочник синтаксиса конвертера md2gost (Markdown → DOCX по ГОСТ 7.32-2017 и ТЗ РТУ МИРЭА). Всё, что здесь описано, извлечено из кода парсера, нумератора и проверки, а не из пользовательских инструкций.
|
||||
|
||||
Базовый парсер — Marko с расширением GFM. Всё, что не перечислено ниже как расширение, работает как обычный GitHub Flavored Markdown.
|
||||
|
||||
---
|
||||
|
||||
## 1. Расширения относительно обычного Markdown
|
||||
|
||||
| Конструкция | Смысл |
|
||||
|-------------|-------|
|
||||
| `# *ЗАГОЛОВОК` | раздел без автоматической нумерации |
|
||||
| `[TOC]` | точка сборки содержания |
|
||||
| `%id Подпись` | метка и подпись следующего объекта |
|
||||
| `` | метка и подпись рисунка-файла |
|
||||
| `@Тип:id` | перекрёстная ссылка с автоподстановкой номера |
|
||||
| `[1]: описание` | запись списка источников |
|
||||
| `[1]`, `[2, 3]`, `[2, c. 21-25]` | ссылка на источник в тексте |
|
||||
| `^` / `>` в ячейке таблицы | объединение ячеек |
|
||||
| ` ```uml `, ` ```mermaid `, ` ```idef0 `, ` ```dfd `, ` ```uml-<схема>` | диаграмма, отрисовываемая конвертером |
|
||||
| ` ```title ` | данные для автогенерации титульного листа |
|
||||
| `+listing`, `+landscape` в строке `%` | флаги оформления объекта |
|
||||
|
||||
---
|
||||
|
||||
## 2. Заголовки
|
||||
|
||||
Синтаксис: от нуля до трёх ведущих пробелов, затем `#`…`######`, пробел, текст.
|
||||
|
||||
Регулярное выражение парсера: `^ {0,3}(#{1,6})((?=\s)[^\n]*?|[^\n\S]*)(?:(?<=\s)(?<!\\)#+)?[^\n\S]*$`
|
||||
|
||||
**Звёздочка** первым непробельным символом текста заголовка делает раздел ненумерованным и удаляется из видимого текста:
|
||||
|
||||
```markdown
|
||||
# *ВВЕДЕНИЕ → раздел без номера
|
||||
# 1 Анализ → нумерованный раздел
|
||||
```
|
||||
|
||||
Пробел после `#` обязателен для корректной работы проверки (`^(#{1,6})\s+(\*?)(.+)$`).
|
||||
|
||||
### Спецразделы
|
||||
|
||||
Распознаются по тексту заголовка, приведённому к прописным. Список из кода проверки:
|
||||
|
||||
`СОДЕРЖАНИЕ`, `ВВЕДЕНИЕ`, `ЗАКЛЮЧЕНИЕ`, `СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ`, `СПИСОК ИСПОЛЬЗУЕМЫХ ИСТОЧНИКОВ`, `ПРИЛОЖЕНИЕ`, `ПРИЛОЖЕНИЯ`, а также любой заголовок, начинающийся на `ПРИЛОЖЕН`.
|
||||
|
||||
Для H1 из этого набора звёздочка обязательна — иначе ошибка `heading.numbered_special`.
|
||||
|
||||
Оформление в DOCX:
|
||||
|
||||
| Заголовок H1 | Выравнивание |
|
||||
|--------------|--------------|
|
||||
| `СОДЕРЖАНИЕ`, `СПИСОК ИСПОЛЬЗОВАННЫХ/ИСПОЛЬЗУЕМЫХ ИСТОЧНИКОВ` | по центру |
|
||||
| `ВВЕДЕНИЕ`, `ЗАКЛЮЧЕНИЕ`, `ПРИЛОЖЕНИЯ`, нумерованные разделы | слева с абзацным отступом |
|
||||
|
||||
`СОДЕРЖАНИЕ` исключается из собираемого оглавления.
|
||||
|
||||
### Нумерация заголовков
|
||||
|
||||
| Режим CLI | Поведение |
|
||||
|-----------|-----------|
|
||||
| `--heading-numbering manual` (по умолчанию) | цифры остаются из markdown: `# 1 …`, `## 1.1 …`; автонумерация Word отключена |
|
||||
| `--heading-numbering auto` | ведущие цифры (`^\d+(?:\.\d+)*\.?\s+`) снимаются с текста заголовка, номера ставит многоуровневый список Word |
|
||||
|
||||
При `manual` номера пиши сам. При `auto` их можно не писать — но безопаснее всегда писать и запускать конвертер в `manual`.
|
||||
|
||||
### `[TOC]`
|
||||
|
||||
Блочный элемент, шаблон `\[(TOC)\]+`. **Только прописными** — `[toc]` не распарсится (хотя проверка его увидит и замечания не выдаст). Ставится отдельной строкой, обычно сразу под `# *СОДЕРЖАНИЕ`.
|
||||
|
||||
Режимы сборки: `--toc native` (по умолчанию, поле Word, обновляется при открытии документа) и `--toc manual` (md2gost сам считает страницы).
|
||||
|
||||
---
|
||||
|
||||
## 3. Метки объектов: строка `%`
|
||||
|
||||
Шаблон: `\%(\w+)( (.+))?` — блочный элемент, прерывает открытый абзац.
|
||||
|
||||
- `id` = `\w+`: буквы, цифры, `_`. **Дефис, точка, пробел внутри `id` недопустимы.** Практическое правило: `[A-Za-z][A-Za-z0-9_]*`.
|
||||
- Подпись необязательна: `%eq1` — валидная строка.
|
||||
- Метка применяется к **следующему непустому блоку**. Пустые строки между меткой и объектом допустимы.
|
||||
- Если подряд идут две метки, к объекту привяжется вторая.
|
||||
- `%` в середине абзаца — обычный текст, не метка.
|
||||
|
||||
### Флаги в строке подписи
|
||||
|
||||
Шаблоны: `(?i)(?:^|\s)\+?listing\b` и `(?i)(?:^|\s)\+?landscape\b`.
|
||||
|
||||
| Флаг | Действие |
|
||||
|------|----------|
|
||||
| `+listing` | у диаграммы дополнительно выводится Листинг с её исходником (получает свой номер) |
|
||||
| `+landscape` | объект размещается на альбомной странице |
|
||||
|
||||
Важные следствия из регулярных выражений:
|
||||
|
||||
1. Плюс необязателен: `listing` без плюса тоже сработает как флаг.
|
||||
2. Регистр не важен: `+Landscape` = `+landscape`.
|
||||
3. Флаги вырезаются из текста подписи в любом месте строки. **Поэтому подпись не должна содержать самостоятельных слов `listing` и `landscape`** — они исчезнут из подписи, а объект получит нежелательное оформление.
|
||||
|
||||
### Метка рисунка-файла
|
||||
|
||||
Метка задаётся в title изображения:
|
||||
|
||||
```markdown
|
||||

|
||||
```
|
||||
|
||||
Тот же шаблон `%(\w+)( (.+))?` и те же флаги.
|
||||
|
||||
### Куда попадает подпись
|
||||
|
||||
| Объект | Положение подписи в DOCX |
|
||||
|--------|--------------------------|
|
||||
| Рисунок и диаграмма | под объектом |
|
||||
| Таблица | надпись над таблицей |
|
||||
| Листинг | надпись над листингом |
|
||||
| Формула | номер в скобках справа от формулы |
|
||||
|
||||
---
|
||||
|
||||
## 4. Перекрёстные ссылки `@Тип:id`
|
||||
|
||||
Шаблон: `@(\w+):(\w+)`. Пробелы вокруг двоеточия недопустимы. Знаки препинания после ссылки не съедаются, поэтому `@Рисунок:arch.` и `@Таблица:cmp,` работают корректно.
|
||||
|
||||
Принимаемые типы (регистр не важен при разрешении ссылки):
|
||||
|
||||
| Тип | Синонимы |
|
||||
|-----|----------|
|
||||
| `Рисунок` | `figure` |
|
||||
| `Таблица` | `table` |
|
||||
| `Листинг` | `listing` |
|
||||
| `Формула` | `formula`, `equation` |
|
||||
|
||||
В документе ссылка разворачивается в «Тип Номер», например `Рисунок 1.1`. Неизвестная метка даёт в тексте `Рисунок?` — визуальный маркер ошибки; проверка сообщает `ref.dangling`.
|
||||
|
||||
Каноническая форма — русские названия: `@Рисунок:id`, `@Таблица:id`, `@Листинг:id`, `@Формула:id`.
|
||||
|
||||
### Соответствие меток и ссылок
|
||||
|
||||
Проверка сверяет два множества:
|
||||
|
||||
- метки — из `^%(\w+)` и из title картинок;
|
||||
- ссылки — из `@[\wА-Яа-я]+:(\w+)`.
|
||||
|
||||
Метка без ссылки → предупреждение `ref.unused`. Ссылка без метки → ошибка `ref.dangling`. Правило простое: **сколько меток, столько и адресатов ссылок**.
|
||||
|
||||
---
|
||||
|
||||
## 5. Нумерация объектов
|
||||
|
||||
Области нумерации задаются профилем `--type`:
|
||||
|
||||
| Область | Профили | Вид номера |
|
||||
|---------|---------|------------|
|
||||
| `section` | `practice`, `coursework`, `vkr`, `APID_coursework` | `1.1`, `2.3`; в приложении — `Б.1` |
|
||||
| `continuous` | `PIS_custom` | `1`, `2`, `3` на весь документ |
|
||||
|
||||
Счётчик раздела увеличивается только на **нумерованных H1** (то есть на `# 1 …`, а не на `# *ВВЕДЕНИЕ`).
|
||||
|
||||
Номер получают все объекты **кроме формул**: формуле номер присваивается, только если в исходном тексте есть `@Формула:id` (или `@Formula:id`) с её меткой. Формула без ссылки остаётся без номера.
|
||||
|
||||
Диаграмма с флагом `+listing` получает два номера под одной меткой: номер Рисунка и номер Листинга. Ссылки `@Рисунок:id` и `@Листинг:id` разрешатся каждая в свой номер.
|
||||
|
||||
---
|
||||
|
||||
## 6. Таблицы
|
||||
|
||||
Синтаксис GFM: строка заголовков, строка-разделитель, строки данных. Выравнивание через `:---`, `:---:`, `---:` поддерживается. Экранированный `\|` не считается границей столбца.
|
||||
|
||||
### Объединение ячеек
|
||||
|
||||
| Маркер | Смысл |
|
||||
|--------|-------|
|
||||
| `^` или `^^` | ячейка — продолжение ячейки **сверху** (rowspan) |
|
||||
| `>` или `>>` | ячейка — продолжение ячейки **слева** (colspan) |
|
||||
|
||||
Маркер должен быть **единственным** содержимым ячейки после обрезки пробелов. Ограничения проверки: маркеры запрещены в строке заголовков (`table.merge_header`), `>` запрещён в первом столбце (`table.merge_col`).
|
||||
|
||||
```markdown
|
||||
%tab_req Требования к системе
|
||||
|
||||
| Категория | Требование |
|
||||
|-----------|------------|
|
||||
| Производительность | обработка 1000 заявок в час |
|
||||
| ^ | время отклика не более 2 с |
|
||||
| Надёжность | доступность 99,5 % |
|
||||
```
|
||||
|
||||
### Правила ТЗ
|
||||
|
||||
- Графа «№ п/п» запрещена (`table.nopp`, шаблон `№\s*п\s*/\s*п`).
|
||||
- Таблицу из одной строки данных не оформляют таблицей.
|
||||
- Таблицы и рисунки не размещают внутри списков.
|
||||
|
||||
### Продолжение на следующей странице
|
||||
|
||||
DOCX не умеет сам подписывать «Продолжение Таблицы N». Режимы `--table-continuation` и `--listing-continuation`: `word` (по умолчанию; после сохранения Word COM режет по реальной пагинации), `off`, `soft`, `legacy`, `caption`. Режим `word` требует Windows, Microsoft Word и пакет `pywin32`.
|
||||
|
||||
---
|
||||
|
||||
## 7. Листинги
|
||||
|
||||
Любой fenced-блок с языком, который **не** является языком диаграммы, становится Листингом.
|
||||
|
||||
~~~markdown
|
||||
%code_volume Процедура расчёта объёма
|
||||
|
||||
```python
|
||||
def volume(count: int, size: float) -> float:
|
||||
return count * size
|
||||
```
|
||||
~~~
|
||||
|
||||
Подсветка синтаксиса включается ключом `--syntax-highlighting` (по умолчанию выключена).
|
||||
|
||||
### Ловушка: автозамена внутри кода
|
||||
|
||||
Препроцессор применяет типографские правки ко всему тексту, **исключая только блоки диаграмм**. Блоки кода не защищены:
|
||||
|
||||
| В исходнике | В DOCX |
|
||||
|-------------|--------|
|
||||
| `print("hello")` | `print(«hello»)` |
|
||||
| `x = a - b` | `x = a — b` |
|
||||
|
||||
Обход: одинарные кавычки в примерах кода и вычитание без пробелов (`a-b`). Ключ `--emdash-to-hyphen` меняет направление замены тире, но кавычки правит всегда.
|
||||
|
||||
---
|
||||
|
||||
## 8. Формулы
|
||||
|
||||
| Форма | Шаблон | Назначение |
|
||||
|-------|--------|------------|
|
||||
| Выключная | `\$\$([\S\s]*?)\$\$` | отдельный блок, нумеруемая формула |
|
||||
| Инлайновая | `\$(.*?)\$` | обозначения в тексте: `$N$` |
|
||||
|
||||
```markdown
|
||||
%eq_volume
|
||||
$$
|
||||
V = N \times S
|
||||
$$
|
||||
|
||||
где $N$ — число записей; $S$ — средний размер записи, байт.
|
||||
|
||||
Оценка по (@Формула:eq_volume) используется при выборе хранилища.
|
||||
```
|
||||
|
||||
Номер присваивается только при наличии ссылки. Расшифровка обозначений начинается со слова «где» без двоеточия.
|
||||
|
||||
---
|
||||
|
||||
## 9. Диаграммы
|
||||
|
||||
Fenced-блок с языком диаграммы превращается в Рисунок; исходник отрисовывается конвертером.
|
||||
|
||||
### Встроенные языки
|
||||
|
||||
| Оградка | Движок |
|
||||
|---------|--------|
|
||||
| `uml`, `plantuml` | PlantUML (java + `plantuml.jar`, иначе Kroki) |
|
||||
| `mermaid`, `mmd` | локальный Mermaid (браузер / QuickJS), иначе Kroki |
|
||||
| `idef0`, `uml-idef0` | локальный рендер на Pillow, без Java и сети |
|
||||
| `dfd`, `uml-dfd`, `data-flow-diagram`, `yourdon`, `uml-yourdon` | пакет `data-flow-diagram` + Graphviz, иначе Kroki |
|
||||
|
||||
### Схемы из `md2gost.schemes.json`
|
||||
|
||||
Оградка `uml-<id>` или просто `<id>`, где `<id>` — идентификатор схемы (`^[a-z][a-z0-9_]*$`). Встроенные схемы:
|
||||
|
||||
| id | Что это | Оградка |
|
||||
|----|---------|---------|
|
||||
| `c4` | C4 Container | ` ```uml-c4 ` / ` ```c4 ` |
|
||||
| `c4context` | C4 System Context | ` ```uml-c4context ` |
|
||||
| `c4component` | C4 Component | ` ```uml-c4component ` |
|
||||
| `usecase` | UML Use Case | ` ```uml-usecase ` / ` ```usecase ` |
|
||||
| `archimate` | ArchiMate 3.2 | ` ```uml-archimate ` / ` ```archimate ` |
|
||||
|
||||
В теле схемы пишутся **только макросы**: обёртку `@startuml` … `@enduml` и `!include` подставляет схема. В блоке ` ```uml ` наоборот — `@startuml` … `@enduml` обязательны.
|
||||
|
||||
Неизвестный `uml-<id>` даёт ошибку рендера: `Схема диаграммы «<id>» не найдена`.
|
||||
|
||||
Подробный синтаксис всех нотаций — в `diagrams.md`, ArchiMate — в `archimate.md`.
|
||||
|
||||
---
|
||||
|
||||
## 10. Источники
|
||||
|
||||
### Запись списка
|
||||
|
||||
Шаблон строки: `^\[(\d+(?:\.\d+)?)\]:\s*(.+)$`
|
||||
|
||||
Записи размещаются под заголовком, совпадающим с `^#\s*\*?\s*СПИСОК\s+ИСПОЛЬЗОВАНН?ЫХ\s+ИСТОЧНИКОВ\s*$` — то есть «СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ» или «СПИСОК ИСПОЛЬЗУЕМЫХ ИСТОЧНИКОВ».
|
||||
|
||||
```markdown
|
||||
# *СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
|
||||
|
||||
[1]: Иванов И. И. Проектирование информационных систем: учебник. — М.: Инфра-М, 2023. — 320 с.
|
||||
|
||||
[2]: ГОСТ 7.32-2017. Отчёт о научно-исследовательской работе. — М.: Стандартинформ, 2018. — 32 с.
|
||||
```
|
||||
|
||||
Препроцессор сам расставит пустые строки между записями, но безопаснее писать их сразу.
|
||||
|
||||
Для ВКР список делится подразделами `##`; ключ записи получает вид `[1.5]` — номер раздела и номер внутри раздела.
|
||||
|
||||
### Ссылка в тексте
|
||||
|
||||
Шаблон: `\[(\d+(?:\.\d+)?(?:\s*,\s*\d+(?:\.\d+)?)*)(?:\s*,\s*[cс]\.\s*[\d\-–]+)?\]`
|
||||
|
||||
| Форма | Пример |
|
||||
|-------|--------|
|
||||
| одиночная | `[1]`, `[1.5]` |
|
||||
| перечисление | `[2, 3]`, `[2,3]` — **нельзя** писать `[2], [3]` |
|
||||
| с указанием страниц | `[2, c. 21-25]`, `[2.18, с. 21–25]` — буква `c` латинская или русская |
|
||||
|
||||
В DOCX номер становится внутренней гиперссылкой на запись списка.
|
||||
|
||||
Сноски `[^1]` запрещены (`footnote`).
|
||||
|
||||
---
|
||||
|
||||
## 11. Приложения
|
||||
|
||||
```markdown
|
||||
# *ПРИЛОЖЕНИЯ
|
||||
|
||||
Приложение А — Листинг модуля обработки заявок
|
||||
Приложение Б — Схема базы данных
|
||||
|
||||
## Приложение А Листинг модуля обработки заявок
|
||||
|
||||
## Приложение Б Схема базы данных
|
||||
```
|
||||
|
||||
- Заголовок приложения распознаётся шаблоном `^ПРИЛОЖЕНИЕ\s+([А-ЯA-ZЁ])(?:\s+(.+))?$` без учёта регистра. Оформляется как Heading 3 по центру с разрывом страницы; название — отдельной строкой под ним.
|
||||
- Допустимые буквы: `АБВГДЕЖИКЛМНПРСТУФХЦШЩЭЮЯ`. Запрещённые: `Ё З Й О Ч Ь Ы Ъ` (ошибка `appendix.letter`).
|
||||
- Если букву не указать или указать запрещённую, конвертер подставит следующую допустимую по порядку.
|
||||
- При двух и более приложениях после `# *ПРИЛОЖЕНИЯ` нужен перечень обычным текстом, иначе предупреждение `appendix.toc`. Если перечня нет, конвертер вставит его сам, но лучше писать явно.
|
||||
- Объекты внутри приложения нумеруются с буквой приложения: `Рисунок Б.1`.
|
||||
|
||||
---
|
||||
|
||||
## 12. Блок ` ```title `
|
||||
|
||||
Первый блок с оградкой `title` в файле вырезается до разбора markdown и используется для автогенерации титульного листа (только при `--auto-title`).
|
||||
|
||||
````markdown
|
||||
```title
|
||||
number: 1
|
||||
year: 2026
|
||||
```
|
||||
|
||||
# *СОДЕРЖАНИЕ
|
||||
|
||||
[TOC]
|
||||
````
|
||||
|
||||
Тело — плоские строки `ключ: значение`. Принимаемые ключи:
|
||||
|
||||
| Поле | Ключи |
|
||||
|------|-------|
|
||||
| Институт | `institute`, `институт`, `инститиут` |
|
||||
| Кафедра | `department`, `кафедра` |
|
||||
| Дисциплина | `discipline`, `дисциплина` |
|
||||
| Преподаватель | `teacher`, `преподаватель`, `преподователь` |
|
||||
| Должность преподавателя | `teacher_position`, `должность`, `должность преподавателя` |
|
||||
| Номер работы | `number`, `номер`, `№`, `n` — по умолчанию `1` |
|
||||
| Год | `year`, `год` — по умолчанию текущий |
|
||||
|
||||
ФИО и группа студента **из блока не читаются**: они берутся из профиля приложения или ключей `--student` / `--group`.
|
||||
|
||||
Остальные академические поля можно держать в файле `info_conv.yaml` рядом с `.md`; значения из блока ` ```title ` сильнее.
|
||||
|
||||
---
|
||||
|
||||
## 13. Автоматические правки текста
|
||||
|
||||
Препроцессор применяет ко всему тексту, **кроме блоков диаграмм**:
|
||||
|
||||
| Правило | Было | Стало |
|
||||
|---------|------|-------|
|
||||
| Русские кавычки | `"текст"` | `«текст»` |
|
||||
| Тире вместо дефиса между словами | `слово - слово` | `слово — слово` |
|
||||
| Разделение записей списка источников | склеенные строки `[n]:` | каждая записью-абзацем |
|
||||
|
||||
Кавычки не заменяются, если содержимое начинается с `%` (защита строк подписей).
|
||||
|
||||
Ключ `--emdash-to-hyphen` включает обратную замену: все `—` становятся `-`.
|
||||
|
||||
---
|
||||
|
||||
## 14. Прочее
|
||||
|
||||
| Конструкция | Поведение |
|
||||
|-------------|-----------|
|
||||
| `---`, `***`, `___` | по умолчанию **игнорируются**; с ключом `--hr-pagebreak` дают разрыв страницы |
|
||||
| Обычные `**жирный**`, `*курсив*`, `` `код` ``, ссылки, списки | работают как в GFM |
|
||||
| HTML-блоки | не поддерживаются, вставляются как предупреждение в документ |
|
||||
| Комментарии | синтаксиса комментариев нет |
|
||||
| Заголовок глубже `###` | парсится, но в оформлении ТЗ не предусмотрен |
|
||||
| Дефис в обычном тексте | превращается в неразрывный дефис |
|
||||
|
||||
---
|
||||
|
||||
## 15. Полный минимальный пример
|
||||
|
||||
~~~markdown
|
||||
# *СОДЕРЖАНИЕ
|
||||
|
||||
[TOC]
|
||||
|
||||
# *ВВЕДЕНИЕ
|
||||
|
||||
Цель работы — спроектировать модуль учёта заявок.
|
||||
|
||||
# 1 Анализ предметной области
|
||||
|
||||
Обобщённая архитектура приведена на @Рисунок:fig_arch.
|
||||
|
||||

|
||||
|
||||
Сравнение подходов сведено в @Таблица:tab_cmp.
|
||||
|
||||
%tab_cmp Сравнение подходов к реализации
|
||||
|
||||
| Подход | Трудоёмкость | Гибкость |
|
||||
|--------|--------------|----------|
|
||||
| Монолит | низкая | низкая |
|
||||
| Микросервисы | высокая | высокая |
|
||||
|
||||
Модульная структура описана в источнике [1].
|
||||
|
||||
# 2 Проектирование
|
||||
|
||||
Оценка объёма данных выполняется по (@Формула:eq_volume).
|
||||
|
||||
%eq_volume
|
||||
$$
|
||||
V = N \times S
|
||||
$$
|
||||
|
||||
где $N$ — число записей; $S$ — средний размер записи, байт.
|
||||
|
||||
Реализация приведена в @Листинг:code_volume.
|
||||
|
||||
%code_volume Процедура расчёта объёма
|
||||
|
||||
```python
|
||||
def volume(count: int, size: float) -> float:
|
||||
return count * size
|
||||
```
|
||||
|
||||
# *ЗАКЛЮЧЕНИЕ
|
||||
|
||||
Спроектирован модуль учёта заявок, оценён объём хранилища.
|
||||
|
||||
# *СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
|
||||
|
||||
[1]: Иванов И. И. Проектирование информационных систем: учебник. — М.: Инфра-М, 2023. — 320 с.
|
||||
~~~
|
||||
Reference in New Issue
Block a user