444 votes

Est algébrique ou contextuelle en C++ ?

J'entends souvent des réclamations que le C++ est un contexte sensible de la langue. Prenons l'exemple suivant:

a b(c);

Est-ce une définition de variable ou d'une déclaration de fonction? Cela dépend de la signification du symbole c. Si c est une variable, alors a b(c); définit une variable nommée b de type a. Il est initialisée directement avec c. Mais si c est un type, alors a b(c); déclare une fonction nommée b qui prend un c et renvoie un a.

Si vous regardez la définition du contexte langues, il va vous dire que toutes les règles de grammaire doit avoir de gauche qui consistent exactement un non-terminal symbole. Contexte de grammaires, d'autre part, de permettre à des chaînes arbitraires de terminaux et non-terminaux sur le côté gauche.

La navigation par le biais de l'Annexe A de la "Le Langage de Programmation C++", je ne pouvais pas trouver une seule règle de grammaire qui n'avait rien d'autre à part un non-terminal symbole sur son côté gauche. Cela impliquerait que le C++ est libre de tout contexte. (Bien sûr, chaque contexte du langage est également sensible au contexte, dans le sens que le contexte langues forment un sous-ensemble de l'contextuelle langues, mais ce n'est pas le point.)

Alors, est-C++ libre de tout contexte ou contextuelle?

391voto

rici Points 45980

Ci-dessous est ma (courant) préféré démonstration de pourquoi l'analyse C++ est (probablement) Turing-complet, car il montre un programme qui est syntaxiquement correcte si et seulement si un nombre entier est premier.

J'ai donc affirmer que C++ est ni le contexte, ni sensible au contexte.

Si vous autorisez l'arbitraire symbole de séquences sur les deux côtés de la production, de vous produire un Type-0 de la grammaire ("libre") dans la hiérarchie de Chomsky, qui est plus puissant que un contexte sensible de la grammaire; sans restriction grammaires sont Turing-complet. Une sensibilité au contexte (Type 1) de la grammaire permet de multiples symboles du contexte sur le côté gauche de production, mais le même contexte doit apparaître sur le côté droit de la production (d'où le nom de "sensible au contexte"). [1] le Contexte sensible de grammaires sont l'équivalent linéaire borné des machines de Turing.

Dans le programme d'exemple, le premier calcul peut être effectué par un linéaire bornée de la machine de Turing, donc il n'a pas tout à prouver Turing équivalence, mais l'important, c'est que l'analyseur doit exécuter le calcul afin de pouvoir effectuer une analyse syntaxique. Il aurait pu être n'importe quel calcul peut s'exprimer comme une instanciation du modèle et il y a toutes les raisons de croire que le C++ instanciation du modèle est Turing-complet. Voir, par exemple, Todd L. Veldhuizen de 2003 de papier.

Peu importe, C++ peut être analysé par un ordinateur, de sorte qu'il peut certainement être analysé par une machine de Turing. Par conséquent, un droit illimité de grammaire pourrait-il reconnaître. En fait écrire une telle grammaire serait pas pratique, c'est pourquoi la norme n'essayez pas de le faire. (Voir ci-dessous).

Le problème avec "l'ambiguïté" de certaines expressions est surtout un hareng rouge. Pour commencer, l'ambiguïté est une fonction d'une grammaire particulière, pas une langue. Même si une langue peut être prouvé sans équivoque grammaires, si elle peut être reconnue par une grammaire libre de tout contexte, il est libre de tout contexte. De même, si elle ne peut pas être reconnu par un contexte libre de la grammaire, mais il peut être reconnu par un contexte sensible de la grammaire, il est sensible au contexte. L'ambiguïté n'est pas pertinent.

Mais dans tous les cas, à l'instar de la ligne 21 (c - auto b = foo<IsPrime<234799>>::typen<1>();) dans le programme ci-dessous, les expressions ne sont pas ambigus, ils sont tout simplement analysée différemment selon le contexte. Dans l'expression la plus simple de la question, la catégorie syntaxique de certains identifiants dépend de la manière dont ils ont été déclarés (types et fonctions, par exemple), ce qui signifie que la langue officielle aurait à reconnaître le fait que les deux arbitraire des chaînes de longueur dans le même programme sont identiques (déclaration et utilisation). Ce peut être modélisé par la "copie" de la grammaire, qui est la grammaire qui reconnaît deux années consécutives des copies exactes d'un même mot. Il est facile de prouver le lemme de pompage que cette langue n'est pas libre de tout contexte. Un contexte sensible de la grammaire de cette langue est possible, et un Type-0 grammaire est fourni dans la réponse à cette question: http://math.stackexchange.com/questions/163830/context-sensitive-grammar-for-the-copy-language .

Si l'on tentait d'écrire un contexte sensible (ou libre) de la grammaire à l'analyser en C++, il serait tout à fait peut-être remplir l'univers avec des gribouillages. L'écriture d'une machine de Turing pour analyser le C++ serait tout aussi impossible de l'entreprise. Même l'écriture d'un programme C++ est difficile, et autant que je sache, personne n'a été prouvé correct. C'est pourquoi la norme ne tente pas de fournir une grammaire formelle, et pourquoi il choisit d'écrire une partie de l'analyse de règles en anglais technique.

Ce qui ressemble à une grammaire formelle dans la norme C++ n'est pas complète définition formelle de la syntaxe du langage C++. Il n'est même pas complète définition formelle de la langue après le prétraitement, qui pourrait être plus facile à formaliser. (Ce ne serait pas la langue, mais: le langage C++ tel que défini par la norme comprend l'préprocesseur, et le fonctionnement du préprocesseur est décrite de manière algorithmique, car il serait extrêmement difficile de décrire en toute grammaticales formalisme. C'est dans cette section de la norme lexicale de décomposition est décrite, y compris les règles, où il doit être appliqué plus d'une fois.)

Les différentes grammaires (les deux se chevauchent les grammaires pour l'analyse lexicale, qui a lieu avant le prétraitement et l'autre, si nécessaire, par la suite, en plus de la "syntaxique" de la grammaire) sont rassemblés dans l'Annexe A, avec cette remarque importante (italiques ajoutés):

Ce résumé de la syntaxe C++ est destiné à être une aide à la compréhension. Il n'est pas un état précis de la langue. En particulier, la grammaire décrit ici accepte un sur-ensemble de l'valide constructions C++ . Les règles de désambiguïsation (6.8, 7.1, 10.2) doit être appliqué à distinguer les expressions à partir des déclarations. En outre, le contrôle d'accès, l'ambiguïté, et les règles de type doit être utilisé pour éliminer les syntaxiquement valide, mais de sens des constructions.

Enfin, voici la promesse de programme. La ligne 21 est syntaxiquement correct si et seulement si les N en IsPrime<N> est premier. Sinon, typen est un entier, et non pas un modèle, afin typen<1>() est analysée comme (typen<1)>() qui est syntaxiquement incorrecte, car () n'est pas un point de vue syntaxique expression valide.

template<bool V> struct answer { answer(int) {} bool operator()(){return V;}};

template<bool no, bool yes, int f, int p> struct IsPrimeHelper
  : IsPrimeHelper<p % f == 0, f * f >= p, f + 2, p> {};
template<bool yes, int f, int p> struct IsPrimeHelper<true, yes, f, p> { using type = answer<false>; };
template<int f, int p> struct IsPrimeHelper<false, true, f, p> { using type = answer<true>; };

template<int I> using IsPrime = typename IsPrimeHelper<!(I&1), false, 3, I>::type;
template<int I>
struct X { static const int i = I; int a[i]; }; 

template<typename A> struct foo;
template<>struct foo<answer<true>>{
  template<int I> using typen = X<I>;
};
template<> struct foo<answer<false>>{
  static const int typen = 0;
};

int main() {
  auto b = foo<IsPrime<234799>>::typen<1>(); // Syntax error if not prime
  return 0;
}

[1] Pour mettre plus techniquement, chaque production dans un contexte sensible de la grammaire doit être de la forme:

αAβ &rightarrow; αγβ

A est un non-terminal et α, β sont éventuellement vide séquences de symboles de la grammaire, et γ est une séquence non-vide. (La grammaire des symboles peuvent être soit des terminaux ou non terminaux).

Cela peut être lu comme A &rightarrow; γ seulement dans le contexte [α, β]. Dans un contexte (Type 2), de la grammaire, α et β doit être vide.

Il s'avère que vous pouvez également limiter les grammaires avec le "monotone" restriction, où chaque production doit être de la forme:

α &rightarrow; β|α| ≥ |β| > 0 (|α| signifie "la longueur de l' α")

Il est possible de prouver que l'ensemble des langages reconnus par une fonction monotone des grammaires est exactement le même que l'ensemble des langages reconnus par le contexte sensible de grammaires, et c'est souvent le cas que c'est plus facile à la base de preuves sur monotone des grammaires. Par conséquent, il est assez fréquent de voir des "sensible au contexte", utilisé comme s'il signifiait "monotone".

128voto

jpalecek Points 31928

Tout d'abord, vous l'avez fort justement observé qu'il y a pas de règles contextuelles dans la grammaire à la fin du C++ standard, de sorte que la grammaire est libre de tout contexte.

Cependant, que la grammaire n'est pas de décrire avec précision le langage C++, car elle produit non C++ des programmes tels que

int m() { m++; }

ou

typedef static int int;

Le langage C++ définit comme "l'ensemble des bien formé programmes C++" n'est pas libre de tout contexte (il est possible de montrer que le fait de simplement exigeant des variables déclarées en fait). Étant donné que vous pouvez théoriquement écrire Turing-complet des programmes dans les modèles et faire d'un programme un mal formé en fonction de leur résultat, il n'est même pas sensible au contexte.

Maintenant, (ignorant) de personnes (généralement pas la langue des théoriciens, mais analyseur concepteurs utilisent généralement "pas libre de tout contexte" dans certains de la signification suivante

  • ambigu
  • ne peut pas être analysée avec le Bison
  • pas LL(k), LR(k), LALR(k) ou que ce soit par l'analyseur, la langue définie par la classe qu'ils ont choisi

La grammaire à l'arrière de la norme n'a pas satisfait à ces catégories (ie. il est ambigu, pas LL(k)...) donc C++ grammaire "n'est pas libre de tout contexte" pour eux. Et dans un sens, ils ont raison c'est sacrément bien dur pour produire un travail analyseur C++.

Notez que les propriétés utilisée ici ne sont que faiblement lié au contexte des langues - l'ambiguïté n'a rien à voir avec sensibilité au contexte (en fait, sensible au contexte des règles généralement aide à distinguer les productions), les deux autres sont simplement des sous-ensembles de contextes langues. Et l'analyse du contexte de libre-langues n'est pas un processus linéaire (bien que l'analyse déterministe de l'est).

62voto

280Z28 Points 49515

Oui. L'expression suivante a un autre ordre des opérations selon le type résolu contexte:

Edit: Lorsque la commande de fonctionnement varie, il est incroyablement difficile d'utiliser un "régulier" du compilateur qui analyse à un non AST avant de le décorer (la propagation des informations de type). D'autres sensibles au contexte choses mentionnées sont "plutôt facile" par rapport à cela (pas ce modèle d'évaluation est facile).

#if FIRST_MEANING
   template<bool B>
   class foo
   { };
#else
   static const int foo = 0;
   static const int bar = 15;
#endif

Suivi de:

static int foobar( foo < 2 ? 1 < 1 : 0 > & bar );

30voto

Dan Points 1137

Pour répondre à votre question, il faut distinguer deux questions différentes.

  1. La simple syntaxe de presque chaque langage de programmation est libre de tout contexte. Généralement, il est donné comme un extended Backus-Naur form ou context-free grammar rencontrent.

  2. Cependant, même si un programme est conforme avec le contexte-free grammar rencontrent défini par le langage de programmation, il n'est pas nécessairement valide le programme. Il y a beaucoup de non-libre de tout contexte poperties qu'un programme doit satisfaire afin d'être d'un programme valide. E. g., le plus simple de ces biens est la portée des variables.

Pour conclure, si oui ou non C++ est libre de tout contexte dépend de la question que vous vous posez.

10voto

AraK Points 38702

C++ est analysé avec GLR de l'analyseur. Cela signifie que lors de l'analyse du code source, l'analyseur peut rencontrer l'ambiguïté, mais il doit continuer et de décider quelle règle de grammaire à utiliser plus tard.

voir, aussi,

Pourquoi C++ ne peut pas être analysé avec un LR(1) analyseur?


Rappelez-vous que le contexte de la grammaire ne peut pas décrire TOUTES les règles d'un langage de programmation syntaxe. Par exemple, l'Attribut de la grammaire est utilisée pour vérifier la validité d'un type d'expression.

int x;
x = 9 + 1.0;

Vous ne pouvez pas décrire les aspects suivants de la règle de la grammaire libre de tout contexte : Le Côté Droit de la mission doivent être du même type de la Gauche.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X