Регулярное выражение для анализа функций с произвольной глубиной
Я разбираю простой язык (формулы Excel) для функций, содержащихся внутри. Имя функции должно начинаться с любой буквы, за которой следует любое количество букв / цифр, и заканчиваться открытым паренем (без пробелов между ними). Например MyFunc(
, Функция может содержать любые аргументы, включая другие функции, и должна заканчиваться парой )
, Конечно, математика в паренсе разрешена =MyFunc((1+1))
а также (1+1)
не должен быть обнаружен как функция, потому что он не соответствует правилу функции, которое я только что описал. Моя цель - распознавать вызовы функций самого высокого уровня в формуле, определять имя функции, извлекать аргументы. С помощью аргументов я могу рекурсивно искать другие вызовы функций.
Используя этот урок, я взломал следующие регулярные выражения. Никто, кажется, не делает трюк. Они оба терпят неудачу на тестовом примере, вставленном ниже.
Это должно работать, но полностью терпит неудачу:
(?<name>[a-z][a-z0-9]*\()(?<body>(?>[a-z][a-z0-9]*\((?<DEPTH>)|\)(?<-DEPTH>)|.?)*(?(DEPTH)(?!)))\)
Это работает для многих тестовых случаев, но не для тестового примера ниже. Я не думаю, что он правильно обрабатывает вложенные функции - он просто ищет открытых / закрытых парней во вложении:
(?<name>[a-z][a-z0-9]*\()(?<body>(?>\((?<DEPTH>)|\)(?<-DEPTH>)|.?)*(?(DEPTH)(?!)))\)
Вот тест, который ломает их всех:
=Date(Year(A$5),Month(A$5),1)-(Weekday(Date(Year(A$5),Month(A$5),1))-1)+{0;1;2;3;4;5}*7+{1,2,3,4,5,6,7}-1
Это должно соответствовать:
Date(ARGUMENTS1)
Weekday(ARGUMENTS2)
Where ARGUMENTS2 = Date(Year(A$5),Month(A$5),1)
Вместо этого это соответствует:
ARGUMENTS2 = Date(Year(A$5),Month(A$5),1)-1)
Я использую.net RegEx, который обеспечивает внешнюю память.
2 ответа
Это вполне в пределах возможностей.NET регулярных выражений. Вот рабочая демонстрация:
using System;
using System.Text.RegularExpressions;
namespace Test
{
class Test
{
public static void Main()
{
Regex r = new Regex(@"
(?<name>[a-z][a-z0-9]*\()
(?<body>
(?>
\((?<DEPTH>)
|
\)(?<-DEPTH>)
|
[^()]+
)*
(?(DEPTH)(?!))
)
\)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
string formula = @"=Date(Year(A$5),Month(A$5),1)-(Weekday(Date(Year((A$5+1)),Month(A$5),1))-1)+{0;1;2;3;4;5}*7+{1,2,3,4,5,6,7}-1";
foreach (Match m in r.Matches(formula))
{
Console.WriteLine("{0}\n", m.Value);
}
}
}
}
выход:
Дата (Год (A$5), Месяц (A$5),1) Будни (Дата (Год ((A$5+1)),Month(A$5),1))
Основная проблема с вашим регулярным выражением заключалась в том, что вы включали имя функции как часть рекурсивного соответствия - например:
Name1(...Name2(...)...)
Любое открытое имя, которому не предшествовало имя, не учитывалось, потому что оно соответствовало последней альтернативе, |.?
), и это сбило с баланса близких паренов. Это также означало, что вы не можете подобрать формулы =MyFunc((1+1))
, который вы упомянули в тексте, но не включили в пример. (Я бросил дополнительный набор паренов, чтобы продемонстрировать.)
РЕДАКТИРОВАТЬ: Вот версия с поддержкой незначительных, указанных в кавычках:
Regex r = new Regex(@"
(?<name>[a-z][a-z0-9]*\()
(?<body>
(?>
\((?<DEPTH>)
|
\)(?<-DEPTH>)
|
""[^""]+""
|
[^()""]+
)*
(?(DEPTH)(?!))
)
\)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
Возможно, вы захотите посмотреть по этой ссылке: http://www.xtremevbtalk.com/archive/index.php/t-177848.html