Выделение лексем в мат. выражении (при помощи регекспов)?

Есть простенький язык мат. выражений. В нем операции (+-*/ и проч., возможно на два и более символов), переменные, числа (целые, дробные).


Задача — разбить входное выражение (к примеру, «2 + x1 — 12.5 / ((0.24^y) * coeff)») на лексемы (токены) (еще хорошо бы проверить валидность).


Задача классическая, в общем случае решается написанием лексического анализатора (а валидация при помощи написания парсера). На основе Regex-пов я пока решаю её так:

<font color="black"><font color="#0000ff">public</font> <font color="#0000ff">static</font> <font color="#2B91AF">IEnumerable</font>&lt;<font color="#0000ff">string</font>&gt; TokenizeInfix(<font color="#0000ff">string</font> infix)<br/>
{<br/>
&nbsp;&nbsp;infix = Regex.Replace(infix, <font color="#A31515">@&quot;[ \t]+&quot;</font>, <font color="#0000ff">string</font>.Empty);<br/>
<br/>
&nbsp;&nbsp;<font color="#0000ff">var</font> match = Regex.Match(infix, <font color="#A31515">@&quot;[-+*/^%()]|[A-Za-z][A-Za-z0-9]*|[+-]?[0-9]+\.?[0-9]*&quot;</font>);<br/>
<br/>
&nbsp;&nbsp;<font color="#0000ff">if</font> (match.Success)<br/>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">do</font> <font color="#0000ff">yield</font> <font color="#0000ff">return</font> match.Value;<br/>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff">while</font> ((match = match.NextMatch()).Success);<br/>
}</font><br/>
<br/>
<font color="gray">* This source code was highlighted with <a href="http://virtser.net/blog/post/source-code-highlighter.aspx"><font color="gray">Source Code Highlighter</font></a>.</font>


Однако в этом случае у пользователя есть возможность ввести «x 1», и это будет воспринято как одна лексема «x1»:variable. Можно ли учесть все условия в одном регекспе, чтобы не производить предварительное удаление пробельных символов? Как лучше всего проверить валидность выражения?


На уровне рассуждений повыше воспос другой: можно ли обойтисть только регулярками, или лучше заюзать генератор вроде Сосо/R (тогда нужна помощь в описании грамматики, а может даже у кого завалялся .ATG-файлик как раз для моего случая)?
  • Вопрос задан
  • 3136 просмотров
Решения вопроса 1
@Maccimo
Однако в этом случае у пользователя есть возможность ввести «x 1», и это будет воспринято как одна лексема «x1»:variable. Можно ли учесть все условия в одном регекспе, чтобы не производить предварительное удаление пробельных символов?
Предварительно удаляя пробелы вы не упрощаете задачу разбора выражения, а напротив — усложняете.

Уберите удаление пробелов и добавьте вместо этого пробелы в качестве ещё одной альтернативы в
regexp идущий сейчас вторым.

Т.е.:

var match = Regex.Match(infix, @"[-+*/^%()]|[A-Za-z][A-Za-z0-9]*|[+-]?[0-9]+\.?[0-9]*|[ \t]+");

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

При более-менее серьёзной задаче, естественно, стоит написать нормальный лексер, а не городить огород из Regex. И модифицировать его проще будет и работать он будет быстрее.

Как лучше всего проверить валидность выражения?

можно ли обойтисть только регулярками, или лучше заюзать генератор вроде Сосо/R
Задачу проверки корректности арифметического выражения средствами одних толкьо regexp-ов, насколько мне известно, решить нельзя.

Можно задействовать генератор парсеров, а можно написать простенький свой, работающий по методу рекурсивного спуска.

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

Если вас интересует тема парсеров/компиляторов и т.п., то однозначно стоит прочитать «Компиляторы. Принципы, технологии и инструментарий».

Построение парсера арифметических выражений есть там в качестве пошагового примера.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы