То, что вам нужно, - это анализатор вашего языка (Java) и средство определения имен и типов. («Конструктор таблиц символов»).
После синтаксического анализа исходного текста у компилятора обычно есть средство распознавания имен, которое пытается записать определение имен и их соответствующих типов, и средство проверки типов, которое проверяет, что каждое выражение имеет допустимый тип.
Обычно распознаватель имени / типа жалуется, когда не может найти определение. То, что вы хотите сделать, это найти «неопределенную» вещь, которая вызывает проблему, и вывести тип для нее.
Для
IPerson p = new Person();
распознаватель имен знает, что «Person» и «IPerson» не определены. Если бы это было
Foo p = new Bar();
не будет никакой подсказки, что вам нужен интерфейс, просто что Foo является неким абстрактным родителем Bar (например, класс или интерфейс). Таким образом, решение о том, каково это, должно быть известно инструменту («всякий раз, когда вы найдете такую конструкцию, предположите, что Foo является интерфейсом ...»). Вы можете использовать эвристику: IFoo и Foo означают, что IFoo должен быть интерфейсом, и где-то кто-то должен определить Foo как класс, реализующий этот интерфейс. Однажды
инструмент принял это решение, ему необходимо обновить свои таблицы символов, чтобы он мог
перейти к другим утверждениям:
Для
p.Name = "Sklivvz";
учитывая, что p должен быть интерфейсом (согласно предыдущему выводу), тогда Name должно быть элементом поля, и, похоже, его тип - String из присваивания.
С этим утверждение:
Assert.AreEqual("Sklivvz", p.Name);
имена и типы разрешаются без дальнейшей проблемы.
Содержание сущностей IFoo и Foo зависит от вас; вам не нужно было использовать get и set, но это личный вкус.
Это не будет работать так хорошо, если в одном выражении несколько сущностей:
x = p.a + p.b ;
Мы знаем, что a и b являются вероятными полями, но вы не можете угадать, какой числовой тип действительно, если они числовые, или если они являются строками (это допустимо для строк в Java, не знаю о C #).
Для C ++ вы даже не знаете, что означает «+»; это может быть оператор в классе Bar.
Поэтому вам нужно собрать ограничений , например, «a - это какое-то неопределенное число или строка» и т. Д., И, поскольку инструмент собирает доказательства, он сужает набор возможных ограничений. (Это работает, как те проблемы со словом: «У Джо семь сыновей. Джефф выше Сэма. Гарри не может спрятаться за Сэмом ... кто близнец Джеффа?», Где вы должны собрать доказательства и устранить невозможность) Вам также нужно беспокоиться о случае, когда вы столкнетесь с противоречием.
Вы можете исключить вариант p.a + p.b, но тогда вы не сможете безнаказанно писать свои модульные тесты. Существуют стандартные решатели ограничений, если вы хотите безнаказанности. (Что за концепция).
Хорошо, у нас есть идеи, теперь, можно ли это сделать на практике?
Первая часть этого требует анализатора и сгибаемого имени и преобразователя типов. Вам нужен решатель ограничений или, по крайней мере, операция «определенное значение течет к неопределенному значению» (тривиальный решатель ограничений).
Наш набор инструментов для реинжиниринга программного обеспечения DMS с его Java Front End мог бы сделать это. DMS - это инструмент для создания инструментов, предназначенный для людей, которые хотят создавать инструменты, которые обрабатывают компьютерные языки произвольным образом. (Подумайте о «вычислениях с фрагментами программы, а не с числами»).
DMS предоставляет механизм синтаксического анализа общего назначения и может построить дерево для любого внешнего интерфейса, который ему предоставляется (например, Java, и есть внешний интерфейс C #).
Причина, по которой я выбрал Java, заключается в том, что в нашем интерфейсе Java есть все эти механизмы разрешения имен и типов, и он предоставляется в исходной форме, поэтому его можно сгибать. Если вы остановились на тривиальном решателе ограничений, вы, вероятно, могли бы согнуть решатель имен Java, чтобы выяснить типы. DMS позволит вам собирать деревья, которые соответствуют фрагментам кода, и объединять их в более крупные; поскольку ваш инструмент собирал факты для таблицы символов, он мог строить примитивные деревья.
Где-то вы должны решить, что все готово. Сколько юнит-тестов должен увидеть инструмент
прежде чем он знает весь интерфейс? (Я думаю, это съедает все, что вы предоставляете?).
После завершения он собирает фрагменты для различных членов и создает AST для интерфейса; DMS может использовать свой prettyprinter для преобразования этого AST обратно в исходный код, как вы показали.
Я предлагаю Java здесь, потому что наш интерфейс Java имеет разрешение имен и типов. Наш интерфейс C # не делает. Это «простой» вопрос амбиций; кто-то должен написать один, но это довольно много работы (по крайней мере, для Java, и я не могу представить, что C # действительно отличается).
Но идея в принципе отлично работает с использованием DMS.
Вы можете сделать это с другой инфраструктурой, которая предоставит вам доступ к анализатору и гибкому преобразователю имен и типов. Это может быть не так легко получить для C #; Я подозреваю, что MS может предоставить вам анализатор и доступ к разрешению имен и типов, но никак не изменить это. Может быть, моно ответ?
Вам все еще нужно было сгенерировать фрагменты кода и собрать их. Вы можете попытаться сделать это путем взлома строк; Мой (долгий) опыт склеивания программных фрагментов состоит в том, что, если вы делаете это со строками, вы в конечном итоге создаете беспорядок. Вы действительно хотите фрагменты, которые представляют фрагменты кода известного типа, которые могут быть объединены только так, как позволяет грамматика; DMS делает это, таким образом, не беспорядок.