Как стать автором
Обновить

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

Время на прочтение3 мин
Количество просмотров7.2K

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

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

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

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

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

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

Я также решил добавить три зарезервированные конструкции:
{:String} соответствует выражению: (("(\\.|[^"])*")|('(\\.|[^'])*'))
{:Number} соответствует выражению: (-?[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|Number|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}

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

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

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

Теги:
Хабы:
Всего голосов 7: ↑3 и ↓4+1
Комментарии20

Публикации

Истории

Работа

QT разработчик
13 вакансий
Программист C++
115 вакансий

Ближайшие события

12 – 13 июля
Геймтон DatsDefense
Онлайн