Рекурсивные регулярные выражения

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

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

Стал делать и подумал, а почему бы мне не добавить туда своих тараканов. Я решил добавить две конструкции:

{namesubexpression} - вызов под выражения по имени "namesubexpression",
($namesubexpression:BodyExpression) - описание под выражения с именем "namesubexpression".

Само описание под выражения может встречаться в любом месте структуры регулярного выражения и игнорируется при поиске, подобно закоментированым: (#MeComment).
Сразу же возникает проблема бесконечной рекурсии.
Вот пример рекурсивного регулярного выражения, который недопустим: ($E:{E}){E}

Конечно, я сделал стадию валидации и такие поисковые конструкции просто не допустятса в поисковую машину. Также валидацию не пройдет выражение, которое содержит в себе вызов не описанного под выражения.

Вот пример текста, который можно спарсить рекурсивным регулярным выражением (РРВ): [[[[[A]]]]]
А вот его РРВ: ($RRE:\[({RRE}|A)\]){RRE}

Я также решил добавить три зарезервированные конструкции:
{:String} соответствует выражению: (("(\\.|[^"])*")|('(\\.|[^'])*'))
{:Digit} соответствует выражению: (-?[0-9]+.?[0-9]*[Ee]?-?[0-9]*)
{:Name} соответствует выражению: ([A-Za-z][A-Za-z0-9]*)
Но их поисковая система не использует структурные элементы аналогичных выражений, а организованна встроенным машинным поиском, который работает значительно быстрее и возвращает одну целую строку текста, в которой содержится всё тело найденного соответствия а не части для каждого компонента в аналогичных регулярных выражениях.

Формат ответа:

Поисковая машина в случае обнаружения хотя бы одного соответствия выдаёт ответ. А именно структуру, которая является множеством элементов, каждый из которых может быть либо строкой текста, либо таким же множеством. Ответом, успешного, поиска гарантированно будет множество. Каждым элементом которого будет множество, которое гарантированно состоит из двух элементов. На первом месте гарантированно стоит строка текста: либо "Text", либо "Expression". Это признак порции ответа. В случае "Text", на втором месте гарантированно будет строка текста. В другом случае, на втором месте гарантированно будет описание найденных соответствий с элементами РРВ. Всякое найденное соответствие разделяет текст, в котором проводится поиск, на части, которые находятся между...

Та короче, вот пример:

Пример кода на языке Автор:

	Expression = ":";
	Data = "SomePrefixText :: SomeInteriorText : SomeSufixText.";
	Result = Expression.RegularExpression("Parse",Data);
	trace(JSON("stringify",Result));

Ответ:

[
	[ "Text", "SomePrefixText " ],
	[ "Expression", [ ":" ] ],
	[ "Expression", [ ":" ] ],
	[ "Text", " SomeInteriorText " ],
	[ "Expression", [ ":" ] ],
	[ "Text", " SomeSufixText." ]
]

Вот второй пример:

	Expression = "($RRE:\\(({RRE}|A+)\\)){RRE}";
	Data = "((AAAA))";
	Result = Expression.RegularExpression("Parse",Data);
	trace(JSON("stringify",Result));

Ответ:

[
	[
		"Expression",
		[
			[
				"SubExpression:RRE",
				[
					"(",
					[ [ [ "SubExpression:RRE", [ "(", [ [ [ "A", "A", "A", "A" ] ] ], ")" ] ] ] ],
					")"
				]
			]
		]
	]
]

Теоретически с помощью одного хорошо сделанного РРВ, можно описать синтаксис любого языка программирования, в котором нет предпроцессора.

Так выглядит PHP: ((<\?(php)?.*?\?>)+|(^<\?(php)?.*))
:-)
Но недостатком такого подхода является невозможность отследить синтаксические ошибки.

А вот ещё один пример.
Ради спортивного интереса я создал и протестировал РРВ, которым можно описать любое РРВ и само себя. Полагаю вам будет интересно взглянуть:

(?x)
($CallSE:\{({:Name}|:(String|Digit|Name))\})
($SpecialChar:\\[DSWdsw])
($SpaceChar:([ \f\n\r\t\v]|\\[fnrtv]))
($x:\\[0-9A-Fa-f]{2})
($O:\\[0-7]{3})
($onec:({x}|{O}|\\.|[^\[\]\\\^]))
($ones:({x}|{O}|\\.|[^\[\]\\\^$|?*+\(\){}]))
($Char:\[\^?({SpecialChar}|({onec}-{onec})|{onec})*?-?\])
($Qantifikator:([\?\*\+]|\{[0-9]*(,[0-9]*)?\})[\?\+]?)
($Grupa:\\[1-9])
($QE:\\Q.*?\\E)
($PosInStr:([\^\$]|\\[Bb]))
($PrefixBody:((\#|\?#)|(\${:Name}:)|(\?[ismx-]*:)|(\?=)|(\?!)|(\?<=)|(\?<!)|(\?=)|(\?>)|(\?)))
($SubE:\({BodyE}\))
($BodyE:(
	(\?[ismx-]*)|
	(
		{PrefixBody}?
		(
			({SubE}|{Char}|{CallSE}|{Grupa}|{QE}|{PosInStr}|{SpecialChar}|{SpaceChar}|(\|)|{ones}){Qantifikator}?
		)*
	)
))
{BodyE}

Вы можете взглянуть на код этой библиотеки РРВ на С++ тут.
Кому надо, берите и пользуйтесь.

Помните: делать свой язык программирования - самая не благодарная робота.
Но программист пользователь ценится дороже, чем просто пользователь.

Жду ваших комментариев, критики и интересных предложений.

Источник: https://habr.com/ru/post/711942/


Интересные статьи

Интересные статьи

Привет, мы команда СберМегаМаркета, и это обзорная статья о нашей площадке, пробный камень для блога Хабре. За нашими плечами спешный переезд с PHP на GO, ребрендинг и решение таких задач, с которыми ...
Всем привет. Если вы когда-либо работали с универсальными списками в Битрикс24, то, наверное, в курсе, что страница детального просмотра элемента полностью идентична странице редак...
Ваш сайт работает на 1С-Битрикс? Каждому клиенту вы даёте собственную скидку или назначаете персональную цену на товар? Со временем в вашей 1С сложилась непростая логика ценообразования и формирования...
Этот пост будет из серии, об инструментах безопасности, которые доступны в Битриксе сразу «из коробки». Перечислю их все, скажу какой инструмент в какой редакции Битрикса доступен, кратко и не очень р...
От скорости сайта зависит многое: количество отказов, брошенных корзин. Согласно исследованию Google, большинство посетителей не ждёт загрузки больше 3 секунд и уходит к конкурентам. Бывает, что сайт ...