регулярные выражения для анализа исходного кода - PullRequest
1 голос
/ 05 января 2010

У меня интересная проблема. Мне нужно проанализировать исходный код и определить типы переменных до его компиляции. Таким образом, Reflection не может быть использован!

Есть только пять типов:

double      x = 1.23;
long        x = 3;
string     s='Hello World!' 
bool        b=true 
object[]     A = [1, 1+2, 'Hello', s]

Пример кода курса:

for (i=0; i < 5; i++)
{
    a=2;
    b=4;
    c=6;
    tesstClass.Str = 'sss';
}

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

Сначала я найду все фрагменты кода с желаемой переменной (выражения с ней) следующим образом:

string pattern = variable + @"[\w.]*\s*[-*+/]?=\s*[\w\s+'*/-]*\s*;";
MatchCollection mc = Regex.Matches(code, pattern);

Во-вторых, я анализирую каждое совпадение, используя 5 регулярных выражений (по одному для каждого типа):

string stringPattern = @"'[^'\r\n]*'"; //String;
string doublePattern = @"\b[0-9]+\.[0-9]+\b"; //Double
string longPattern = @"[-+]?\b\d+\b"; // Integer with a sign
string boolPattern = @"\b(false|true)\b"; // Boolean
string arrayPattern = @"\[([\w']*\s*,?\s*)*\]"; // Array

Я очень плох в регулярных выражениях. Итак, я определил набор очень простых т. выражения. Можете ли вы помочь мне уточнить их.

Ответы [ 7 ]

2 голосов
/ 05 января 2010

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

Как показал Краген, есть случаи, когда регулярные выражения будут соответствовать некоторому фрагменту исходного кода, но они будут игнорировать контекст, в котором появляется этот фрагмент исходного кода. Это может привести к ошибкам. Хотя может быть возможным для написания более умных регулярных выражений для таких случаев, как показал Краген, они быстро станут чрезвычайно сложными и трудными для чтения / обслуживания / понимания, поскольку им приходится учитывать множество различных возможных контекстов. *

Я бы предпочел написать парсер с использованием генератора парсеров (например, Yacc или Bison). Но в зависимости от языка вашего исходного кода это также может быть довольно сложно.

2 голосов
/ 05 января 2010

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

a = 'b = 3;';

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

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

1 голос
/ 05 января 2010

Учитывая, что это .NET, вы можете рассмотреть возможность использования CodeDOM для его правильного анализа.

Используйте существующего C # провайдера CodeDOM , чтобы получить структурированное представление вашего исходного кода с помощью метода разбора , а затем обойти его. Это позволяет вам создать решение, которое может работать практически на ЛЮБОМ языке .NET.

Даже если вы сказали, что это должно быть сделано до компиляции, вы можете использовать временную компиляцию в памяти, с которой затем можно работать, используя отражение. Там вам может помочь провайдер CodeDOM.

1 голос
/ 05 января 2010

Обычный способ сделать это - получить AST вашей программы, а затем просто найти нужные вам объявления переменных. Грамматики, как было предложено, являются хорошим способом генерации таких AST.

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

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

Сначала , я бы начал с регулярного выражения, подобного этому:

(double|long|string|bool|object)\s*(\[\s*\])?\s+(YOUR_VARIABLE_TOKEN)

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

Я не проверял это регулярное выражение, и оно определенно не идеально. Это было просто, чтобы дать вам идею.

Второй , вам нужно будет проверить эти совпадения с некоторыми исключениями. Например:

  1. Объявление может быть внутри строкового литерала: "bool a;"
  2. Объявление может быть внутри комментария: /* bool a; */

Кроме того, это не очень странный запрос. Eclipse также выполняет такую ​​оценку в некоторых случаях, таких как отступ.

Это непростая задача, хотя, особенно, поиск таких исключительных случаев. Удачи.

1 голос
/ 05 января 2010

Что именно найти?

Вам нужно найти только литералы (константы) или всю декларацию? Можно использовать выражения для поиска литералов, но немного сложнее разобрать весь код

Дайте шанс грамматике

Если вам нужно разобрать весь код ... Знаете ли вы грамматические анализаторы? Когда я изучал «теорию языка», мы использовали грамматики для разбора кода. Вы можете определить базовый анализатор с помощью регулярных выражений для токенов (констант, зарезервированных слов, символов и т. Д.) И использовать грамматический анализатор для всей структуры.

Параметр Java: JavaCC . Должна быть опция .Net.

По сути, грамматический анализатор может анализировать сложные структуры (и иметь «память»). Если автомат с конечным состоянием эквивалентен регулярному выражению, FSA со стеком (это память) эквивалентна грамматике. Обладает большей вычислительной мощностью.

0 голосов
/ 05 января 2010

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

svn co svn: //anonsvn.mono-project.com/source/trunk/mcs

0 голосов
/ 05 января 2010

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

Вы полагаетесь на то, что все типы всегда различны. Что если 0 или 1 назначено логическому значению? Регулярные выражения не очень хороши для сокращения ввода. Ваша программа в лучшем случае выдает список идентификаторов каждого типа. Есть лучшие подходы.

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

Если вы не просто делаете домашнюю работу, у вас должен быть доступ к парсеру для этого языка. Если вы этого не сделаете, вы должны начать его с генератора синтаксических анализаторов, таких как Bison (или что-то еще в стиле).

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


Редактировать: Я забыл сказать, что делать с Bison: vP. У вас есть структура данных для каждой переменной. Он должен содержать набор возможных типов. Скажем, unsigned int с одним битом, представляющим каждый тип, enum type_bits { double_bit = 1, long_bit = 2, string_bit = 4, … };. Начните с установки всех битов в 1; т.е. type_map = (type_bits) -1;. Когда вы сталкиваетесь с каждой операцией, маскируйте биты, которые несовместимы с ней. Когда вы закончите, у вас будет определенное количество битов. Примените правила приоритета, если их больше одного, сгенерируйте ошибку, если их нет.

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