Ce que vous semblez avoir besoin est un analyseur pour votre langage (Java), et un résolveur de noms et de types. ("Constructeur de table de symboles").Après l'analyse du texte source, un compilateur a généralement un résolveur de nom, qui essaye d'enregistrer la définition des noms et de leurs types correspondants, et un vérificateur de type, qui vérifie que chaque expression a un type valide.
Normalement, le résolveur de nom/type se plaint lorsqu'il ne trouve pas de définition. Ce que vous voulez qu'il fasse est de trouver la chose "indéfinie" qui cause le problème, et en déduire un type pour cela.
Pour
IPerson p = new Person();
le resolver sait que "personne" et "IPerson" ne sont pas définis. Si elle était
Foo p = new Bar();
il n'y aurait pas la moindre idée que vous vouliez une interface, juste que Foo est une sorte de parent abstrait de Bar (par exemple, une classe ou une interface). Ainsi, la décision telle qu'elle est doit être connue de l'outil ("chaque fois que vous trouvez une telle construction, supposons que Foo est une interface ..."). Vous pouvez utiliser une heuristique: IFoo et Foo signifient que IFoo devrait être une interface, et quelque part quelqu'un doit définir Foo comme une classe réalisant cette interface. Une fois l'outil a pris cette décision, il serait nécessaire de mettre à jour ses tables de symboles afin qu'il puisse passer à d'autres déclarations:
Pour
p.Name = "Sklivvz";
étant donné que p doit être une interface (par le inférence précédente), alors Name doit être un membre de champ, et il semble que son type soit String de l'affectation.
Avec cela, la déclaration:
Assert.AreEqual("Sklivvz", p.Name);
noms et types
résoudre sans problème.
Le contenu des entités IFoo et Foo dépend de vous. vous n'avez pas à utiliser get and set mais c'est un goût personnel.
Cela ne fonctionnera pas si bien quand vous avez plusieurs entités dans la même déclaration:
x = p.a + p.b ;
Nous savons a et b sont des champs probables, mais vous ne pouvez pas deviner quel type numérique si effectivement ils sont numeric, ou si ce sont des chaînes (ceci est légal pour les chaînes en Java, pas sur C#). Pour C++, vous ne savez même pas ce que «+» signifie; il pourrait s'agir d'un opérateur sur la classe Bar. Donc, ce que vous avez à faire est de collecter contraintes, par exemple, "est un nombre indéfini ou une chaîne", etc et que l'outil recueille des preuves, il réduit l'ensemble des contraintes possibles. (Cela fonctionne comme ces problèmes de mots: "Joe a sept fils Jeff est plus grand que Sam. Harry ne peut pas se cacher derrière Sam. Qui est le jumeau de Jeff?" Où vous devez collecter les preuves et supprimer les impossibilités. Vous devez également vous soucier du cas où vous vous retrouvez avec une contradiction.
Vous pouvez exclure le cas p.a + p.b, mais vous ne pouvez pas écrire vos tests unitaires en toute impunité. Il y a des solutions de résolution standards là-bas si vous voulez l'impunité. (Quel concept). OK, nous avons les idées, maintenant, cela peut-il être fait de façon pratique?
La première partie nécessite un analyseur et un résolveur de nom et de type pliable. Vous avez besoin d'un solveur de contraintes ou au moins d'une opération "valeur définie s'écoule vers une valeur indéfinie" (solveur de contraintes trivial).
Notre DMS Software Reengineering Toolkit avec ses Java Front End pourrait probablement le faire. DMS est un outil de création d'outils destiné aux personnes qui souhaitent créer des outils qui traitent les langages informatiques de manière arbitraire. (Pensez à "calculer avec des fragments de programme plutôt que des nombres"). Le DMS fournit une machine d'analyse d'usage général, et peut construire un arbre pour n'importe quelle extrémité avant (par exemple, Java, et il y a une extrémité avant C#). La raison pour laquelle j'ai choisi Java est que notre frontal Java a toutes ces machines de résolution de noms et de types, et il est fourni sous forme de source afin qu'il puisse être plié. Si vous vous êtes contenté du solveur de contraintes trivial, vous pourriez probablement plier le résolveur de nom Java pour comprendre les types. DMS vous permettra d'assembler des arbres qui correspondent à des fragments de code, et de les fusionner en plus gros; Lorsque votre outil a collecté des faits pour la table des symboles, il a pu construire les arbres primitifs.
Quelque part, vous devez décider que vous avez terminé. Combien de tests unitaires l'outil doit-il voir avant de connaître l'intégralité de l'interface? (Je suppose qu'il mange tous ceux que vous fournissez?). Une fois terminé, il assemble les fragments pour les différents membres et construit un AST pour une interface; DMS peut utiliser son prettyprinter pour convertir AST dans le code source comme vous l'avez montré.
Je suggère Java ici parce que notre frontal Java a une résolution de nom et de type. Notre extrémité avant C# ne fait pas. C'est une "simple" question d'ambition; quelqu'un doit en écrire un, mais c'est beaucoup de travail (du moins c'était pour Java et je ne peux pas imaginer que C# soit vraiment différent).
Mais l'idée fonctionne bien en principe en utilisant DMS.
Vous pouvez le faire avec une autre infrastructure qui vous donne accès à un analyseur et un résolveur de nom et de type pliable. Cela pourrait ne pas être si facile à obtenir pour C#; Je soupçonne que MS peut vous donner un analyseur, et l'accès à la résolution de nom et de type, mais aucun moyen de changer cela. Peut-être que Mono est la réponse?
Vous avez toujours besoin de générer des fragments de code et de les assembler. Vous pourriez essayer de le faire par piratage de chaîne; ma (longue) expérience avec le collage de morceaux de programme est que si vous le faites avec des cordes, vous finirez par en faire un bazar. Vous voulez vraiment des morceaux qui représentent des fragments de code de type connu, qui ne peuvent être combinés que de la façon dont la grammaire le permet; DMS le fait donc pas de gâchis.
Vous pouvez le faire avec un [modèle T4.] (Http://msdn.microsoft.com/fr-fr/library/bb126445.aspx) Je n'ai pas d'exemple, mais je pourrais essayer d'écrire un, si vous êtes intéressé. Je pouvais voir comment cela pourrait être vraiment utile dans le développement de Test-First (TDD); l'astuce consiste à compiler votre code pendant que vous écrivez des tests pour des méthodes qui n'existent pas encore. L'autre problème, bien sûr, est que le modèle T4 doit probablement être plus intelligent que ce que vous avez décrit, et je doute que vous ayez fourni une spécification complète dans votre exemple. –
@RobertHarvey, j'en discutais avec un collègue, et il a l'impression qu'il pourrait y avoir trop d'inférence à faire. Je serais juste heureux avec quelque chose qui fonctionne d'une manière beaucoup plus intelligente que la folie actuelle du «clic droit» :-) – Sklivvz