web-dev-qa-db-fra.com

Qu'est-ce qui rend Java plus facile à analyser que C?

Je connais le fait que les grammaires de C et C++ sont contextuelles , et en particulier vous avez besoin d'un "lexer hack" en C. Par contre, j'ai l'impression que vous pouvez analyser Java avec seulement 2 jetons d'anticipation, malgré une similitude considérable entre les deux langages.

Que devriez-vous changer à propos de C pour le rendre plus maniable à analyser?

Je demande parce que tous les exemples que j'ai vus de la sensibilité au contexte de C sont techniquement admissibles mais terriblement étranges. Par exemple,

foo (a);

pourrait appeler la fonction void foo avec l'argument a. Ou bien, il pourrait déclarer a comme étant un objet de type foo, mais vous pourriez tout aussi bien vous débarrasser des parenthèses. Cette bizarrerie se produit en partie parce que la règle de production du "déclarant direct" pour la grammaire C remplit le double objectif de déclarer à la fois les fonctions et les variables.

D'autre part, la grammaire Java a des règles de production distinctes pour la déclaration de variable et la déclaration de fonction. Si vous écrivez

foo a;

alors vous savez que c'est une déclaration de variable et foo peut être analysé sans ambiguïté en tant que nom de type. Il peut ne pas s'agir d'un code valide si la classe foo n'a pas été définie quelque part dans la portée actuelle, mais il s'agit d'un travail d'analyse sémantique qui peut être effectué lors d'une étape de compilation ultérieure.

Je l'ai vu dire que C est difficile à analyser à cause de typedef, mais vous pouvez déclarer vos propres types dans Java aussi. Quelles règles de grammaire C, outre direct_declarator, sont en faute?

89
korrok

L'analyse de C++ devient difficile. Analyser Java devient de plus en plus difficile.

Voir ceci SO réponse expliquant pourquoi C (et C++) est "difficile" à analyser . Le bref résumé est que C et C++ grammaires sont intrinsèquement ambigus; ils vous donneront plusieurs analyses et vous doit utiliser le contexte pour résoudre les ambiguïtés. Les gens font alors l'erreur de supposer que vous devez résoudre les ambiguïtés lorsque vous analysez; pas le cas, voir ci-dessous. Si vous insistez pour résoudre les ambiguïtés pendant que vous analysez, votre analyseur devient plus compliqué et beaucoup plus difficile à construire; mais cette complexité est une blessure auto-infligée.

IIRC, Java 1.4 La grammaire "évidente" de LALR (1) n'était pas ambiguë, donc elle était "facile" à analyser. Je ne suis pas sûr que le moderne Java n'a pas au moins d'ambiguïtés locales à longue distance; il y a toujours le problème de décider si "... >>" ferme deux modèles ou est un "opérateur de décalage à droite". Je soupçonne moderne = Java n'analyse plus avec LALR (1) .

Mais on peut surmonter le problème d'analyse en utilisant des analyseurs puissants (ou des analyseurs faibles et des hacks de collecte de contexte comme le font principalement les frontaux C et C++), pour les deux langages. C et C++ ont la complication supplémentaire d'avoir un préprocesseur; ceux-ci sont plus compliqués en pratique qu'ils ne le paraissent. Une affirmation est que les analyseurs C et C++ sont si durs qu'ils doivent être écrits à la main. Ce n'est pas vrai; vous pouvez construire Java et les analyseurs C++ très bien avec les générateurs d'analyseurs GLR.

Mais l'analyse n'est pas vraiment là où est le problème.

Une fois que vous aurez analysé, vous voudrez faire quelque chose avec l'arbre AST/analyser. En pratique, il faut savoir, pour chaque identifiant, quelle est sa définition et où il est utilisé ("résolution de nom et de type", de manière bâclée, construction de tables de symboles). Cela s'avère être beaucoup plus de travail que d'obtenir le bon analyseur, aggravé par l'héritage, les interfaces, la surcharge et les modèles, et le fait que la sémantique de tout cela est écrite dans un langage naturel informel réparti sur des dizaines à des centaines de pages. de la norme linguistique. C++ est vraiment mauvais ici. Java 7 et 8 deviennent assez horribles de ce point de vue. (Et les tableaux de symboles ne sont pas tout ce dont vous avez besoin; voir ma biographie pour un essai plus long sur "La vie après l'analyse")) .

La plupart des gens ont du mal avec la partie d'analyse pure (souvent jamais terminée; vérifiez SO lui-même pour les nombreuses, nombreuses questions sur la façon de construire des analyseurs de travail pour de vrais langages), donc ils ne voient jamais la vie après l’analyse. Ensuite, nous obtenons des théorèmes populaires sur ce qui est difficile à analyser et aucun signal sur ce qui.

La correction de la syntaxe C++ ne vous mènera nulle part.

En ce qui concerne la modification de la syntaxe C++: vous constaterez que vous devez corriger beaucoup d'endroits pour prendre en charge la variété des ambiguïtés locales et réelles dans toute grammaire C++. Si vous insistez, la la liste suivante pourrait être un bon point de départ . Je soutiens qu'il est inutile de le faire si vous n'êtes pas le comité des normes C++; si vous le faisiez et construisiez un compilateur en utilisant cela, personne sain d'esprit ne l'utiliserait. Il y a trop d'investissements dans les applications C++ existantes pour passer à la commodité des gars qui construisent des analyseurs; en outre, leur douleur est terminée et les analyseurs existants fonctionnent bien.

Vous voudrez peut-être écrire votre propre analyseur. OK, c'est bon; ne vous attendez pas à ce que le reste de la communauté vous laisse changer la langue qu'ils doivent utiliser pour vous faciliter la tâche. Ils veulent tous que ce soit plus facile pour eux, et c'est d'utiliser le langage tel que documenté et mis en œuvre.

76
Ira Baxter