Проектирование с помощью Расширенных Форм Бэкуса Наура

velkin velkin
В очередной раз задумался, стоит ли использовать при проектировании программ Расширенные Формы Бэкуса Наура (англ. Extended Backus Naur Form), сокращённо РБНФ (англ. EBNF). И дело не только в них, возможно что-то другое подобное, включая различные парсеры.

Локализация ISO/IEC 14977:1996(E) (Extended BNF)

Фактически, если вы можете описать синтаксис языка программирования, который представляет собой инструкции и является ограничением над простым текстом (plain text), то точно так же вы можете описать прочие ограничения, такие как идиомы, паттерны, архитектуры.

Ограничения простого текста.
1. инструкции.
2. идиомы.
3. паттерны.
4. архитектуры.
5. алгоритмы.

Причём в этот список так же входят алгоритмы. Где-то читал, что алгоритмы относятся к шаблонам поведения. Не к шаблонам проектирования поведения, а именно к шаблонам поведения.

Алгоритмы это точно такие же шаблоны как и другие конструкции с точки зрения ограничений. Их можно написать по-разному, но всегда есть предел при заходе за который данный алгоритм перестаёт считаться таковым и очевидно будет назван как-то по другому.

С точки зрения конструирования кода можно выделить операции.
1. Сбор.
2. Разбор.

Сбор используется для конструирования, а разбор для получения исходных конструкций или проверки на правильность набора.

По большому счёту, то о чём я говорю называется метаязыком.

Метаязы́к — язык, предназначенный для описания другого языка, называемого объектным языком.

Я думаю, что каждый программист так или иначе применяет метаязык при конструировании программ, только все ограничения находятся в голове у программиста. Они просто не заданы в явном виде, но они есть. Потом смотришь на их или свой код.

-Видишь суслика.
-Нет.
-И и я не вижу, а он есть.

Кстати, вот почему код не передаёт даже явные задумки, а не только отброшенные варианты. Не факт, что по коду можно создать метаязык использовавшийся для его создания. И как следствие проверить на правильность без самих программистов написавших код тоже нельзя.

1. Если брать ручное создание и проверку, то здесь всё понятно. Берём РБНФ или что ещё и погнали писать правила. Всё делаем ручками, проверяем глазками.
2. А если брать автоматику, то нужен какой-нибудь универсальный парсер. Их так-то не мало, но что-то я так за десятилетия ничего не попробовал на них наваять.

Первое попавшееся.
Нюансы разработки парсера для своего языка программирования

Ведь в жизни практически любого программиста может наступить момент, когда ему в голову приходит светлая идея — разработать свой собственный язык программирования.

В принципе создать метаязык на основе существующих языков таких как C++ или C++ или может быть даже C++ вполне можно было бы. Хотя лично я воспринимаю это лишь как ограничения над конструкциями для получения явных ограничений вопреки неявным ограничениям, которые существуют лишь в мозгах программистов X.

Собственно пока всё это лишь на стадии размышления. Если есть какие-то мысли, практический опыт, видели где-то интересную статью или книгу на эту тему, то поделитесь ниже в комментариях.
alpha21264
alpha21264
15.04.2024 03:56
Здравствуйте, velkin, Вы писали:

V>В очередной раз задумался, стоит ли использовать при проектировании программ Расширенные Формы Бэкуса Наура (англ. Extended Backus Naur Form), сокращённо РБНФ (англ. EBNF). И дело не только в них, возможно что-то другое подобное, включая различные парсеры.


А чего ты хочешь-то вообще?
Ну вот есть такая вещь как DSL
Это не расширение языка программирования, это описание решения задачи на более высокоуровневом языке.

А так, вообще, если ты обладаешь хорошим стилем программирования,
то набор твоих функция становится вот таким специализированным DSL, на которым ты пишешь программу.
velkin
velkin
16.04.2024 10:36
Здравствуйте, alpha21264, Вы писали:

A>А чего ты хочешь-то вообще?

A>Ну вот есть такая вещь как DSL
A>Это не расширение языка программирования, это описание решения задачи на более высокоуровневом языке.

Предметно-ориентированный язык (англ. domain-specific language, DSL — «язык, специфический для предметной области») — компьютерный язык, специализированный для конкретной области применения
..
Примером, показывающим условность классификации, служит язык БНФ (и компилятор с него Lex/Yacc): с одной стороны, это яркий пример метаязыка, с другой — он предназначен для одной конкретной задачи.


Сегодня скачал первую случайно попавшуюся книжку про Flex и Bison. John Levine (примеры ftp://ftp.iecc.com/pub/file/flexbison.zip). В интернете пишут, что GNU Flex/Bison это тоже самое, что и Lex/Yacc, только от GNU, и работает получше. GNU Flex заменяет Lex, а GNU Bison заменяет Yacc.

Скачал виндовые версии с SourceForge, установил. Эти штуки по сути генерируют код на Си из введённых грамматик. Получается я мог бы сам написать лексер и парсер на Си или чём-нибудь другом. А мог бы сгенерить код на Си с помощью Flex/Bison и откомпилировать с помощью того же GNU Compiler Collection (GCC), в Windows его реализация MinGW и так далее.

Теперь к вопросу о том, чего я хочу.

Каких программ вам не хватает?

2) Анализатор текста.
Казалось бы есть куча всяких редакторов текста, токенизаторов, лексеров, парсеров. Но где спрашивается удобное решение. Такие задачи как подмена токенов для чтения текста или кода на другом разговорном языке. Или категоризация и описания токенов и многослойных конструкций, которые они представляют. Единая база по всем кодовым проектам на основе сканирования множества файлов. Собственная синтаксическая подсветка. Текстовый редактор на основе символов, битов и ascii-графики. И всё в таком роде.

Но это ладно, мечта которая видимо может быть реализована лишь собственными силами. Пока забудем про фантазии.

В разных текстовых редакторах есть механизм сниппетов, то есть отрывков (фрагментов) кода.
1. Notepad++.
2. Kate.
3. Dreamweaver.
4. Qt Creator.
И так далее.

Но важно понимать, что где-то сниппеты совсем убогие, где-то получше. В Notepad++ отдельный плагин совсем убогий. В Kate сделано лучше, особенно синтаксис. Ну и в Dreamweaver, Qt Creator и прочих свои нюансы.

Важно вот что, даже если в каком-то редакторе механизм сниппетов более продвинут, в другом это не более, чем кусок кода, то есть там не появляется предложения заполнить созданные специальные формы после создания сниппетов.

Так что в итоге на конкретный редактор полагаться нельзя, так как под рукой может оказаться нечто другое. Разве что некто прямо фанат конкретного текстового редактора.

В течении дня я порассуждал и вот что подумал.

Взять для примера.

1. Cниппеты Kate.

${field_name} создает простое редактируемое поле. Все последующие появления одного и того же field_name создают поля, которые отражают содержимое первого во время редактирования.

${field_name=default} можно использовать для указания значения по умолчанию для поля. default может быть любым выражением JavaScript.

Используйте ${field_name=text}, чтобы указать фиксированную строку в качестве значения по умолчанию.

${func(other_field1,other_field2, ...)} можно использовать для создания поля, которое оценивает функцию JavaScript при каждом редактировании и содержит его содержимое. Дополнительную информацию смотрите на вкладке «Скрипты».

${cursor} можно использовать для обозначения конечной позиции курсора после того, как все остальное было заполнено.


2. И взять Extended Backus Naur Form.

Применение Обозначения Альтернатива Значение
определение =
конкатенация ,
прекращение ; .
чередование | / или !
необязательный [ ... ] (/ ... /) ни одного или единожды
повторение { ... } (: ... : ) ни одного или больше
группировка ( ... )
терминальная строка "..."
терминальная строка '...'
комментарий (* ... *)
особая последовательность ? ... ?
исключение -
Кстати, интересно, что повторение ни одного или более, то есть для гарантии вхождения надо дописывать группу или аналог вроде лексемы (нетерминала) и текста (терминала).
Расширенная Форма Бэкуса Наура.

лексема «::=» её описание (или «=»)
'…' — текстовый элемент — символ или группа символов
A, B — элемент A, за которым следует элемент B (конкатенация)
A | B — либо элемент A либо B (выбор)
[A] — элемент A входит или не входит (условное вхождение)
{A} — ноль или более элементов A (повторение)
(A B) — группировка элементов


А теперь берём из головы минимальную программу на C++ 03 по версии Страуструпа.
int main(){}

Как из вот такого кода сделать РБНФ. Начну с того, что обёртывать текст в одинарные или двойные кавычки лишь вредит ведь всё изначально является текстом. Лучше уж оборачивать лексемы при их создании.

Потому здесь на мой взгляд больше подходят сниппеты. В Kate это знак доллара $ и элемент в фигурных скобках.

И здесь мне пришла в голову мысль, а что если использовать все виды скобок.
1. () круглые скобки.
2. [] квадратные скобки.
3. {} фигурные скобки.
4. <> треугольные скобки.

Последних нет в РБНФ, я их добавил для компании.

Для примера мне нужно гарантированное одно вхождение и чтобы точно знать где его начало, а где конец.

Глобальная подпрограмма.
void $(название)()
{
    ${инструкция}
}

А название это соответственно идентификатор, далее стандартное описание идентификатора посредством допустимых символов.

Можно было бы написать по английски прямо так, что это идентификатор.
void $(identifier)()
{
    ${statement}
}

В конце концов люди будут писать или для себя, или по выведенному для какой-то компании или проекта стандарту.

Или возьмём подпрограммы и главную программу. В C++ подпрограммы это функции не возвращающие значение, то есть return на void и не имеющие параметров и аргументов пустые скобки (). Функция main соответственно главная программа.
${глобальная подпрограмма}

$(главная программа)

Добавим глобальные значения, но ограничимся лишь переменными, то есть не константами или чем-то другим.
${глобальная переменная}

${глобальная подпрограмма}

$(главная программа)

И дадим возможность пользоваться stl. А могли бы и не дать, то есть явно подразумевается разрешить что-то или нет.
${включение stl}

${глобальная переменная}

${глобальная подпрограмма}

$(главная программа)

А это уже получился шаблон архитектуры, который можно многократно уточнить насчёт объявления, определения, инициализации и прочих вещей, таких как разрешение или запрет локальных и статических переменных.

Прямо из головы немного бреда по полученному шаблону архитектуры программы.
#include <iostream>

int power = 0;

void vote_for_trump() { power += 5; }

void ban_russia() { power += 10; }

void ban_china() { power += 15; }

void make_usa_great_again()
{
    vote_for_trump();
    ban_russia();
    ban_china();
}

int main()
{
    make_usa_great_again();
    std::cout << power << std::endl;
}

Опять же для примера.

Разрешено.
1. главная программа.
2. глобальная переменная.
3. глобальная подпрограмма.
4. включение stl.

Запрещены.
1. блоки. { {} }
2. ветвление. if else.
3. циклы. do while for.
и так далее.

Собственно смысл в том, что ну не то чтобы справится любой дурачок, вот прямо реальный дурачок, но по таким конструкциям людям работать будет куда как проще, чем с указанием "сделай мне красиво". Я даже не говорю о валидаторе, который мог бы проверить правильность архитектуры.

Речь хотя бы про то, чтобы выйти из режима, а у меня всё в голове, ме ме ме, я всё помню, да да да. А потом приезжает бас фактор.

Фактор автобуса (англ. bus factor, либо truck factor) проекта — это мера сосредоточения информации среди отдельных членов проекта; фактор показывает количество участников проекта, после потери которых (в оригинале — «попадания» которых под автобус или грузовик, варианты: увольнения, заболевания, рождения у них ребёнка, наступления несчастного случая и других форс-мажорных обстоятельств) проект не сможет быть завершён оставшимися участниками.

И в целом мне думается проще работать с явно записанными конструкциями относящимися как я уже написал выше к инструкциям, идиомам, паттернам, архитектурам, алгоритмам и чем-то ещё.

Отсюда и возникает потребность хоть в чём-то. Даже не обязательно супер крутым технологиям. Можно даже что-то своё завелосипедить, всяко лучше, чем чужие неподходящие решения.

И опять же можно обсудить, кто что делал по этому поводу. У программистов как правило одни и те же основные проблемы. Конечно, если у них нет в чём-то потребности, то и проблем нет.

И вот пожалуйста готово обсуждение "лучших практик", ну или " лучших говно практик", как уж получилось.

Кстати, это ещё нужно для понимания отличия архитектур и прочих конструкций друг от друга.

Например я запрещу глобальные подпрограммы, и прочее включая структуры, классы, но разрешу и даже буду поощрять использовать блоки кода.
${включение stl}

${глобальная переменная}

$(главная программа)

Главная программа
int main()
{
    ${блок кода}
}

Блок кода.
${инструкция}
${
{
    $(блок кода)
}
}

Может где-то ошибся, здесь просто нужна рекурсия, но сейчас лень проверять. Надо по идее посмотреть как принято писать в РБНФ.

И пример по данной архитектуре.
#include <iostream>

int power = 0;

int main()
{
    // make_usa_great_again
    {
        // vote_for_trump
        {
            power += 5;
        }
        // ban_russia
        {
            power += 10;
        }
        // ban_china
        {
            power += 15;
        }
    }
    std::cout << power << std::endl;
}

Конечно, нужно кучу уточнений в синтаксисе по инструкциям и прочему, чтобы всё было чётко. Но опять же надо учитывать разрешённые и запрещённые языковые и созданные из них конструкции.
velkin
velkin
16.04.2024 09:56

Формы Велкина (Velkin form)


1. История.
2. Описание.
2.1 Повторение.
2.2. Порядок.
2.3. Вложение.
2.4. Текст.
3. Продолжение.

История


В общем я тут почитал себя и википедию.

Расширенная форма Бэкуса — Наура (расширенная Бэкус — Наурова форма (РБНФ)) (англ. Extended Backus–Naur Form (EBNF)) — формальная система определения синтаксиса, в которой одни синтаксические категории последовательно определяются через другие. Используется для описания контекстно-свободных формальных грамматик. Предложена Никлаусом Виртом. Является расширенной переработкой форм Бэкуса — Наура, отличается от БНФ более «ёмкими» конструкциями, позволяющими при той же выразительной способности упростить и сократить в объёме описание.

Получается, что фактически это формы Вирта (Wirth). Поиск по Wirth Form выдал мне Wirth syntax notation.

Синтаксическая нотация Вирта (WSN) — это метасинтаксис, то есть формальный способ описания формальных языков. Первоначально предложен Никлаусом Виртом в 1977 году в качестве альтернативы форме Бэкуса – Наура (BNF). Он имеет несколько преимуществ перед BNF, поскольку содержит явную конструкцию итерации и позволяет избежать использования явного символа для пустой строки (например, <пусто> или ε).


Причём смотрите, в изначальных формах Бэкуса-Наура используются треугольные скобки.
<правпосл>::=<пусто> | (<правпосл>) | <правпосл><правпосл>

Это к вопросу моего комментария выше.

И здесь мне пришла в голову мысль, а что если использовать все виды скобок.
1. () круглые скобки.
2. [] квадратные скобки.
3. {} фигурные скобки.
4. <> треугольные скобки.

Последних нет в РБНФ, я их добавил для компании.

Короче, если есть формы Бэкуса-Наура, уж не знаю кто их там изобрёл, а есть формы Вирта, то очевидно должны быть и формы Велкина.

Эволюция форм.
1. Формы Бэкуса-Наура (Backus–Naur form).
2. Формы Вирта (Wirth form).
3. Формы Велкина (Velkin form).

Или syntax notation вместо form, если вам так больше нравится.

Описание


Начнём с простой теории.

Повторение


Количество повторений.
1. 0.
2. 1.
3. ∞.

Повторение.
Синтаксис Минимальное Максимальное
[] 0 1
{} 0
() 1 1
<> 1
Диапазоны "0,0" и "∞,∞" лишены смысла. Остальные сочетания приведены выше.

Те кто читал мою статью Конспектирование на смартфоне могли заметить, что для выделения чётких структур я использую нумерованные или ненумерованные списки с разделителем точкой.

Такая форма записи так же позволяет создавать таблицы или древовидные списки. Её преимущество в том, что можно структурировать данные используя лишь простой текст без языков разметки.

Повторение.
Синтаксис. Минимальное. Максимальное.
1. []. 0. 1.
2. {}. 0. ∞.
3. (). 1. 1.
4. <>. 1. ∞.

Понятно, что возникает вопрос, а зачем изменять формы скобок, когда можно было бы прописывать диапазоны, причём вместо знака бесконечности использовать букву n для совместимости с ASCII, но здесь есть как плюсы, так и минусы.

Плюсы.
1. Можно записать любое количество повторений.
2. Можно даже записать комбинированные диапазоны, вроде 1-3,7-8,90-n.

Минусы.
1. Диапазоны отвлекают от чтения кода.

Потому вопрос читателям, встречали ли вы когда-нибудь код, который требовал бы повториться отличное число раз от 0, 1 или ∞, причём не в конкретном, а именно абстрактном случае, когда описывается синтаксис.

Порядок


Порядок.
1. Последовательный. ,,,.
2. Параллельный. |||.

Можете сами придумать термин, "поток" или ещё что.

Пример последовательного порядка.
a,b,c,d,e

Пример параллельного порядка.
a|b|c|d|e

Пример последовательно-параллельного порядка.
a|b,c|d,e

В последовательно-параллельном порядке приоритет имеет параллельный порядок, точно так же как в арифметике (по-русски числах) приоритет имеют умножение и деление, над сложением и вычитанием.

Так что можете читать это пример как.
(a|b),(c|d),(e)

Читать то вы можете, но в синтаксисе круглые скобки уже заняты в разделе повторение и такое не будет использоваться.

По идее я не уверен, что это нужно, потому что.

1. Последовательность можно описать как.
$(a)$(b)$(c)$(d)$(e)

2. А параллельность можно описать как.
1. a.
2. b.
3. c.
4. d.
5. e.

Вложение


Суть идеи вкладывать одно в другое. По сути это лексемы или нетерминалы.

Вот только в отличие от форм Бэкуса-Наура или Вирта.
1. У меня всё изначально является текстом.
2. И лишь то, что выделено символом доллара $ в последующих скобках разного типа повторений является вложениями.

Продумывал варианты и вывел такое правило.

Вложение во вложение запрещено


Допустимо.
$()$()

Недопустимо.
$($())

Раздел порядка по сути тоже под вопросом. Я пишу это всё для того, чтобы потом в случае чего можно было отбросить. А не так, что втихаря отбросить наработки Вирта, потом забыть и не понимать почему ничего не работает.

Я двигаюсь от общего к частному, а не от частного к общему, как сделают нормальные люди, то есть сначала проверят теорию на примерах, а потом выведут общность, а не наоборот как у меня, потому что принадлежу к поколению абстракционистов. Хотя до этого я тоже дойду, только позже.

Текст


Текст, он же терминал. Правила для него следующие.

Всё является текстом, если не указано иное


Очевидно, если мы возьмём кусок кода, чтобы создать из него конструкцию (инструкция, идиома, паттерн, архитектура, алгоритм и т.д.) для копипасты и проверки правильности (верификации) ввода, он изначально будет текстом. А вот потом он уже может быть преобразован в синтаксический сниппет.

Текст во вложениях подобен правилам C/C++.
1. Символ. '_'.
2. Строка. "текст".

Если не указаны кавычки, тогда во вложении находится название другого вложения, то есть лексемы или нетерминала.

Продолжение


Пока всё, продолжение следует (to be continued). В нём мы узнаем сработает ли данная задумка или нет.
Pauel
Pauel
25.04.2024 08:20
Здравствуйте, velkin, Вы писали:

V>

Формы Велкина (Velkin form)


Вы изобретаете ABNF, только громоздкий.