Le C ++ est-il sans contexte ou sensible au contexte?


405

J'entends souvent des affirmations selon lesquelles le C ++ est un langage contextuel. Prenons l'exemple suivant:

a b(c);

S'agit-il d'une définition de variable ou d'une déclaration de fonction? Cela dépend de la signification du symbole c. Si cest une variable , a b(c);définit alors une variable nommée bde type a. Il est directement initialisé avec c. Mais si cest un type , alors a b(c);déclare une fonction nommée bqui prend un cet retourne un a.

Si vous recherchez la définition de langages sans contexte, cela vous indiquera essentiellement que toutes les règles de grammaire doivent avoir des côtés gauches constitués d'exactement un symbole non terminal. Les grammaires contextuelles, en revanche, autorisent des chaînes arbitraires de symboles terminaux et non terminaux sur le côté gauche.

En parcourant l'annexe A de "Le langage de programmation C ++", je n'ai pas pu trouver une seule règle de grammaire qui avait autre chose qu'un seul symbole non terminal sur son côté gauche. Cela impliquerait que C ++ est sans contexte. (Bien sûr, chaque langue sans contexte est également contextuelle en ce sens que les langues sans contexte forment un sous-ensemble des langues contextuelles, mais ce n'est pas le but.)

Alors, le C ++ est-il sans contexte ou sensible au contexte?


12
@CarlNorum Veuillez me montrer une règle de grammaire unique de C ++ qui ne se compose pas d'un seul symbole non terminal sur son côté gauche et je vous croirai immédiatement.
fredoverflow

9
IIUC, cela dépend un peu de l'endroit où vous tracez la ligne pour la sensibilité au contexte. Je pense que j'ai vu des gens affirmer que presque tous les langages de programmation typés statiquement sont sensibles au contexte, non pas parce que vous ne pouvez pas construire un compilateur pratique pour eux avec des outils d'analyse CFG, mais parce que de telles implémentations "trichent" en analysant certains programmes invalides et ne les rejetant que plus tard, lors de la vérification de type. Donc, si vous considérez que les programmes mal typés ne sont pas dans la langue (au sens CS, c'est-à-dire un ensemble de chaînes) que l'analyseur devrait accepter, plus de langues que C ++ sont contextuelles.

6
@DeadMG: Non, vous vous trompez. Il n'y a pas du tout "d'analyse" ou de "sémantique" dans la théorie du langage formel, juste un "langage" qui est un ensemble de chaînes.
jpalecek

27
Jusqu'à présent, aucune réponse n'a réellement répondu à votre définition de «grammaire hors contexte». À mon avis, la bonne réponse à cette question cite une production dans l'annexe A qui ne correspond pas à votre définition ou démontre que votre définition est incorrecte ou insuffisante. Défend ton territoire!
Courses de légèreté en orbite le

8
Voir La grammaire de D est-elle vraiment sans contexte? . En fait, je pense que tout le monde ici devrait lire cette question et ses réponses!
Courses de légèreté en orbite le

Réponses:


341

Ci-dessous est ma démonstration préférée (actuelle) de la raison pour laquelle l'analyse C ++ est (probablement) Turing-complète , car elle montre un programme qui est syntaxiquement correct si et seulement si un entier donné est premier.

J'affirme donc que C ++ n'est ni sans contexte ni sensible au contexte .

Si vous autorisez des séquences de symboles arbitraires des deux côtés d'une production, vous produisez une grammaire de type 0 ("sans restriction") dans la hiérarchie Chomsky , qui est plus puissante qu'une grammaire contextuelle; les grammaires libres sont Turing-complete. Une grammaire contextuelle (Type-1) autorise plusieurs symboles de contexte sur le côté gauche d'une production, mais le même contexte doit apparaître sur le côté droit de la production (d'où le nom "context-sensitive"). [1] Les grammaires contextuelles sont équivalentes aux machines de Turing à limites linéaires .

Dans l'exemple de programme, le calcul principal pourrait être effectué par une machine de Turing linéaire, donc cela ne prouve pas tout à fait l'équivalence de Turing, mais l'important est que l'analyseur doit effectuer le calcul afin d'effectuer une analyse syntaxique. Il aurait pu s'agir de n'importe quel calcul exprimable sous forme d'instanciation de modèle et il y a tout lieu de croire que l'instanciation de modèle C ++ est Turing-complete. Voir, par exemple, l'article de Todd L. Veldhuizen de 2003 .

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

Le problème de «l'ambiguïté» de certaines expressions est principalement un hareng rouge. Pour commencer, l'ambiguïté est une caractéristique d'une grammaire particulière, pas une langue. Même s'il peut être prouvé qu'une langue n'a pas de grammaires sans ambiguïté, si elle peut être reconnue par une grammaire sans contexte, elle est sans contexte. De même, si elle ne peut pas être reconnue par une grammaire hors contexte mais qu'elle peut être reconnue par une grammaire contextuelle, elle est contextuelle. L'ambiguïté n'est pas pertinente.

Mais de toute façon, comme la ligne 21 (ie auto b = foo<IsPrime<234799>>::typen<1>();) dans le programme ci-dessous, les expressions ne sont pas du tout ambiguës; ils sont simplement analysés différemment selon le contexte. Dans l'expression la plus simple du problème, la catégorie syntaxique de certains identificateurs dépend de la façon dont ils ont été déclarés (types et fonctions, par exemple), ce qui signifie que le langage formel devrait reconnaître le fait que deux chaînes de longueur arbitraire dans les mêmes programmes sont identiques (déclaration et utilisation). Cela peut être modélisé par la grammaire "copie", qui est la grammaire qui reconnaît deux copies exactes consécutives du même mot. C'est facile à prouver avec le lemme de pompageque cette langue n'est pas sans contexte. Une grammaire contextuelle pour cette langue est possible, et une grammaire de type 0 est fournie dans la réponse à cette question: /math/163830/context-sensitive-grammar-for-the- copie-langue .

Si l'on tentait d'écrire une grammaire contextuelle (ou non restreinte) pour analyser le C ++, cela remplirait très probablement l'univers de gribouillages. Écrire une machine de Turing pour analyser le C ++ serait une entreprise tout aussi impossible. Même l'écriture d'un programme C ++ est difficile, et pour autant que je sache, aucun n'a été prouvé correct. C'est pourquoi la norme n'essaie pas de fournir une grammaire formelle complète, et pourquoi elle choisit d'écrire certaines des règles d'analyse en anglais technique.

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

Les différentes grammaires (deux grammaires qui se chevauchent pour l'analyse lexicale, l'une qui a lieu avant le prétraitement et l'autre, si nécessaire, par la suite, plus la grammaire "syntaxique") sont rassemblées dans l'annexe A, avec cette note importante (non souligné dans l'original):

Ce résumé de la syntaxe C ++ est destiné à être une aide à la compréhension. Ce n'est pas un énoncé exact de la langue . En particulier, la grammaire décrite ici accepte un sur - ensemble de constructions C ++ valides . Des règles de désambiguïsation (6.8, 7.1, 10.2) doivent être appliquées pour distinguer les expressions des déclarations. De plus, les règles de contrôle d'accès, d'ambiguïté et de type doivent être utilisées pour éliminer les constructions syntaxiquement valides mais dénuées de sens.

Enfin, voici le programme promis. La ligne 21 est syntaxiquement correcte si et seulement si N in IsPrime<N>est premier. Sinon, il typens'agit d'un entier et non d'un modèle, il typen<1>()est donc analysé comme (typen<1)>()étant syntaxiquement incorrect car il ()ne s'agit pas d'une expression syntaxiquement 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 le dire plus techniquement, chaque production dans une grammaire contextuelle doit être de la forme:

αAβ → αγβ

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

Cela peut être lu A → γuniquement dans le contexte [α, β]. Dans une grammaire sans contexte (Type 2), αet βdoit être vide.

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

α → β|α| ≥ |β| > 0  ( |α|signifie "la longueur de α")

Il est possible de prouver que l'ensemble des langues reconnues par les grammaires monotones est exactement le même que l'ensemble des langues reconnues par les grammaires contextuelles, et il arrive souvent qu'il soit plus facile de baser les preuves sur des grammaires monotones. Par conséquent, il est assez courant de voir «sensible au contexte» utilisé comme s'il signifiait «monotone».


27
Donc, non seulement il est sensible au contexte, mais il peut être fait en fonction de tout contexte que vous pouvez exprimer dans des modèles, qui sont Turing-complete.
Puppy

7
@mehrdad, l'OP dit "langage contextuel", pas de grammaire contextuelle. L'ambiguïté est une caractéristique d'une grammaire, pas une langue. La langue est en effet contextuelle, mais pas parce qu'une grammaire particulière est ambiguë.
rici

2
Notez que mon exemple n'est pas ambigu. C'est une expression non ambiguë d'un programme valide. Si vous modifiez la valeur à la ligne 21, elle peut devenir mal formée. Mais dans aucun cas, il n'est ambigu.
rici

5
J'ai un doute: comme vous le montrez, le résultat d'une évaluation de modèle peut faire la différence entre un programme bien formé et un programme mal formé. L'évaluation du modèle est terminée. Donc, déterminer correctement si une chaîne est dans le langage (C ++) ne nécessiterait-il pas une complétude complète? Comme vous le dites, un langage contextuel est "juste" un "automate borné linéaire", qui n'est pas un AFAIK complet. Ou votre argument utilise-t-il les limites que la norme C ++ impose à certaines choses, notamment la profondeur d'évaluation des modèles?

4
@AntonGolov: Ma version originale de cet exemple ne faisait que cela (vous pouvez y parvenir en le mettant à l' 0intérieur (), pour un simple), mais je pense que c'est plus intéressant de cette façon, car cela démontre que vous avez besoin de l'instanciation du modèle même pour reconnaître si une chaîne est un programme C ++ syntaxiquement correct. Si les deux branches se compilent, je devrais travailler plus fort pour contester l'argument selon lequel la différence est "sémantique". Curieusement, bien que je sois souvent mis au défi de définir "syntaxique", personne n'a jamais proposé une définition de "sémantique" autre que "des choses que je ne pense pas être syntaxiques" :)
rici

115

Tout d'abord, vous avez observé à juste titre qu'il n'y a pas de règles contextuelles dans la grammaire à la fin de la norme C ++, de sorte que la grammaire est hors contexte.

Cependant, cette grammaire ne décrit pas précisément le langage C ++, car elle produit des programmes non C ++ tels que

int m() { m++; }

ou

typedef static int int;

Le langage C ++ défini comme "l'ensemble des programmes C ++ bien formés" n'est pas dépourvu de contexte (il est possible de montrer que le simple fait de demander des variables à déclarer le rend possible). Étant donné que vous pouvez théoriquement écrire des programmes complets de Turing dans des modèles et rendre un programme mal formé en fonction de leur résultat, il n'est même pas sensible au contexte.

Maintenant, les gens (ignorants) (généralement pas les théoriciens du langage, mais les concepteurs d'analyseurs) utilisent généralement "non context-free" dans certaines des significations suivantes

  • ambigu
  • ne peut pas être analysé avec Bison
  • pas LL (k), LR (k), LALR (k) ou quelle que soit la classe de langage définie par l'analyseur qu'ils ont choisie

La grammaire à l'arrière de la norme ne satisfait pas ces catégories (c'est-à-dire qu'elle est ambiguë, pas LL (k) ...) donc la grammaire C ++ n'est "pas sans contexte" pour eux. Et dans un sens, ils ont raison, il est sacrément difficile de produire un analyseur C ++ fonctionnel.

Notez que les propriétés utilisées ici ne sont que faiblement connectées aux langages sans contexte - l'ambiguïté n'a rien à voir avec la sensibilité au contexte (en fait, les règles contextuelles aident généralement à lever l'ambiguïté des productions), les deux autres ne sont que des sous-ensembles de contexte -langues libres. Et l'analyse des langages sans contexte n'est pas un processus linéaire (bien que l'analyse des langages déterministes le soit).


7
ambiguity doesn't have anything to do with context-sensitivityC'était aussi mon intuition, donc je suis heureux de voir quelqu'un (a) d'accord, et (b) l'expliquer là où je ne pouvais pas. Je crois que cela disqualifie tous les arguments qui sont basés sur a b(c);, et satisfont partiellement la question originale dont la prémisse était des affirmations "souvent entendues" de sensibilité au contexte étant dues à l'ambiguïté ... surtout quand pour la grammaire il n'y a en fait aucune ambiguïté même dans le MVP.
Courses de légèreté en orbite le

6
@KonradRudolph: Ce que dit la norme est "Il existe une quantité définie par l'implémentation qui spécifie la limite de la profondeur totale des instanciations récursives, ce qui pourrait impliquer plusieurs modèles. Le résultat d'une récursion infinie dans l'instanciation n'est pas défini." (14.7.1p15) J'interprète cela comme signifiant qu'une implémentation n'est pas requise pour comprendre tous les programmes c ++ valides, et non que les programmes avec une profondeur de récursivité trop grande ne sont pas valides. Les seuls marqués comme invalides sont ceux avec une profondeur de récursion infinie.
rici

3
@KonradRudolph: Je conteste qu'il s'agit d'une "référence générale". Le fait que j'ai lu cet article assez complexe et que je ne le comprends pas suffisamment pour expliquer ce petit fait devrait suffire à le démontrer. Ce n'est pas comme si vous aviez dit quelque chose comme «les ordinateurs utilisent généralement l'électricité» ou «les bits peuvent être vrais ou faux».
Courses de légèreté en orbite le

3
Si ce fait est vraiment si largement connu, je pense qu'il serait beaucoup plus facile d'en trouver une référence que de discuter longuement de l'opportunité d'en fournir une ou non. Sans parler de constructif.
Samuel Edwin Ward

5
Pour autant que je sache, @Konrad s'est trompé quand il a dit: "Le contexte est équivalent à Turing complet." (du moins, il l'était s'il dénotait "Récursivement énumérable" par "Turing complet"), et n'a depuis lors pas pu reconnaître cette erreur. Voici une référence pour la relation d'inclusion d'ensemble appropriée impliquée ici: en.wikipedia.org/wiki/Chomsky_hierarchy
pnkfelix

61

Oui. L'expression suivante a un ordre d'opérations différent selon le type de contexte résolu :

Edit: Lorsque l'ordre réel des opérations varie, il est extrêmement difficile d'utiliser un compilateur "normal" qui analyse un AST non décoré avant de le décorer (propagation des informations de type). D'autres éléments sensibles au contexte mentionnés sont "plutôt faciles" par rapport à cela (pas que l'évaluation du modèle soit du tout facile).

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

Suivi par:

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

Pourquoi ce problème ne peut-il pas être résolu comme pour C, en se souvenant des définitions de type qui sont dans la portée?
Blaisorblade

1
@Blaisorblade: Une façon de rendre un compilateur "propre" consiste à séparer les tâches en étapes indépendantes dans une chaîne, telles que la création d'un arbre d'analyse à partir de l'entrée suivie d'une étape qui effectue l'analyse de type. C ++ vous oblige à 1) fusionner ces étapes en une ou 2) analyser le document selon les deux / toutes les interprétations possibles, et permettre aux étapes de résolution de type de le réduire à l'interprétation correcte.
Sam Harwell

@ 280Z28: d'accord, mais c'est aussi le cas pour C; Je pense qu'une bonne réponse à cette question devrait montrer pourquoi le C ++ est pire que C. La thèse liée ici fait cela: stackoverflow.com/a/243447/53974
Blaisorblade

26

Pour répondre à votre question, vous devez distinguer deux questions différentes.

  1. La simple syntaxe de presque tous les langages de programmation est sans contexte. Typiquement, il est donné sous forme de Backus-Naur étendu ou de gramme sans contexte.

  2. Cependant, même si un programme est conforme à la grammaire sans contexte définie par le langage de programmation, ce n'est pas nécessairement un programme valide . Il existe de nombreuses propriétés non contextuelles qu'un programme doit satisfaire pour être un programme valide. Par exemple, la propriété la plus simple est l'étendue des variables.

Pour conclure, que le C ++ soit ou non dépendant du contexte dépend de la question que vous posez.


5
Il est intéressant de noter que vous devez souvent placer le niveau de "simple syntaxe" plus bas que vous ne le pensez, afin d'obtenir un CFG pour votre langage de programmation. Prenez C, par exemple. Vous pourriez penser que la règle de grammaire pour une simple déclaration de variable en C serait VARDECL : TYPENAME IDENTIFIER, mais vous ne pouvez pas l' avoir, car vous ne pouvez pas distinguer les noms de type des autres identificateurs au niveau CF. Autre exemple: au niveau CF, vous ne pouvez pas décider si vous souhaitez analyser a*bune déclaration de variable ( bde type pointeur vers a) ou une multiplication.
LaC

2
@LaC: Oui, merci de l'avoir signalé! Soit dit en passant, je suis sûr qu'il existe un terme technique plus couramment utilisé pour une simple syntaxe . Quelqu'un a-t-il le bon terme?
Dan

4
@Dan: ce dont vous parlez est une approximation de la langue donnée par une grammaire sans contexte. Bien entendu, une telle approximation est par définition exempte de co-contexte. C'est le sens dans lequel la "syntaxe" est souvent utilisée lors de la discussion des langages de programmation.
reinierpost

13

Vous voudrez peut-être jeter un œil à La conception et l'évolution du C ++ , par Bjarne Stroustrup. Dans ce document, il décrit ses problèmes en essayant d'utiliser yacc (ou similaire) pour analyser une version antérieure de C ++, et souhaitant qu'il ait plutôt utilisé une descente récursive.


Ouah merci. Je me demande si cela a vraiment du sens de penser à utiliser quelque chose de plus puissant qu'un CFG pour analyser n'importe quel langage artificiel.
Dervin Thunk

Excellent livre pour comprendre le pourquoi du C ++. Je recommande cela et Lippman's Inside the C ++ Object Model pour comprendre comment C ++ fonctionne. Bien que les deux soient un peu datés, ils sont toujours une bonne lecture.
Matt Price

"Meta-S" est un moteur d'analyse contextuelle de Quinn Tyler Jackson. Je ne l'ai pas utilisé, mais il raconte une histoire impressionnante. Consultez ses commentaires dans comp.compilers et consultez rnaparse.com/MetaS%20defined.htm
Ira Baxter

@IraBaxter: votre x-ref est MIA aujourd'hui - et les références solides au logiciel semblent être insaisissables (la recherche Google ne fournit pas de bonnes pistes, que ce soit avec 'site: rnaparse.com meta-s' ou 'quinn jackson meta- s '; il y a des morceaux, mais meta-s.com mène à un site web non informatif, par exemple).
Jonathan Leffler

@Jonathan: depuis un moment, je viens de remarquer votre plainte. Je ne sais pas pourquoi le lien est mauvais, je pensais que c'était bon quand je l'ai écrit. Quinn était plutôt actif dans les comp.compilers. Google semble devenir floconneux, voici tout ce que je peux trouver: groups.google.com/group/comp.compilers/browse_thread/thread/… IIRC, il a signé des droits sur MetaS pour une tenue à Hawaï à revendre. Étant donné à quel point cela était techniquement étrange, à mon humble avis, cela signait son arrêt de mort. Cela ressemblait à un schéma très intelligent.
Ira Baxter

12

Ouais C ++ est sensible au contexte, très sensible au contexte. Vous ne pouvez pas construire l'arborescence de syntaxe en analysant simplement le fichier à l'aide d'un analyseur sans contexte, car dans certains cas, vous devez connaître le symbole à partir des connaissances précédentes pour décider (c'est-à-dire créer une table de symboles pendant l'analyse).

Premier exemple:

A*B;

Est-ce une expression de multiplication?

OU

Est-ce une déclaration de Bvariable pour être un pointeur de typeA ?

Si A est une variable, alors c'est une expression, si A est de type, c'est une déclaration de pointeur.

Deuxième exemple:

A B(bar);

Est-ce un prototype de fonction prenant un argument de bar type?

OU

Est-ce que déclarer variable Bde type Aet appelle le constructeur de A avecbar constante comme initialiseur?

Vous devez savoir à nouveau si bar s'agit d'une variable ou d'un type de la table des symboles.

Troisième exemple:

class Foo
{
public:
    void fn(){x*y;}
    int x, y;
};

C'est le cas lorsque la construction d'une table de symboles pendant l'analyse n'aide pas car la déclaration de x et y vient après la définition de la fonction. Donc, vous devez d'abord parcourir la définition de classe et regarder les définitions de méthode dans un deuxième passage, pour dire que x * y est une expression, et non une déclaration de pointeur ou autre.


1
A B();est une déclaration de fonction même dans une définition de fonction. Recherchez la plupart des
analyses vexantes

"Vous ne pouvez pas construire l'arborescence de syntaxe en analysant simplement le fichier" FAUX. Voir ma réponse.
Ira Baxter

10

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

regarde aussi,

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


N'oubliez pas que la grammaire sans contexte ne peut pas décrire TOUTES les règles d'une syntaxe de langage de programmation. Par exemple, la grammaire des attributs 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 la règle suivante avec une grammaire sans contexte: le côté droit de l'affectation doit être du même type que le côté gauche.


4
La plupart des analyseurs C ++ n'utilisent pas la technologie d'analyse GLR. GCC ne le fait pas. Certains le font. Voir semanticdesigns.com/Products/FrontEnds/CppFrontEnd.html pour celui qui le fait.
Ira Baxter, le

10

J'ai l'impression qu'il y a une certaine confusion entre la définition formelle de "contextuelle" et l'utilisation informelle de "contextuelle". Le premier a un sens bien défini. Ce dernier est utilisé pour dire "vous avez besoin de contexte pour analyser l'entrée".

Cette question est également posée ici: sensibilité au contexte vs ambiguïté .

Voici une grammaire sans contexte:

<a> ::= <b> | <c>
<b> ::= "x"
<c> ::= "x"

C'est ambigu, donc pour analyser l'entrée "x", vous avez besoin d'un peu de contexte (ou vivre avec l'ambiguïté, ou émettre "Avertissement: E8271 - L'entrée est ambiguë à la ligne 115"). Mais ce n'est certainement pas une grammaire contextuelle.


Comment le fait d'avoir plusieurs symboles sur le côté gauche d'une production résout-il ce problème? Je ne pense pas que cette réponse réponde à la question.
user541686

1
Ma réponse est en réponse à la première phrase: "J'entends souvent dire que C ++ est un langage contextuel." Si ces revendications utilisent l'expression "context-sensitive" de manière informelle, il n'y a pas de problème. Je ne pense pas que C ++ soit formellement sensible au contexte.
Omri Barel

Je pense que C ++ est formellement contextuel, mais le problème que j'ai est que je ne comprends pas comment une grammaire contextuelle aurait plus de succès à analyser C ++ qu'un CFG.
user541686

6

Aucun langage de type algol n'est sans contexte, car il a des règles qui contraignent les expressions et les instructions dans lesquelles les identifiants peuvent apparaître en fonction de leur type, et parce qu'il n'y a pas de limite au nombre d'instructions qui peuvent se produire entre la déclaration et l'utilisation.

La solution habituelle consiste à écrire un analyseur sans contexte qui accepte réellement un surensemble de programmes valides et à mettre les parties contextuelles en ad hoc code "sémantique" attaché aux règles.

C ++ va bien au-delà, grâce à son système de templates Turing-complete. Voir question de dépassement de pile 794015 .




5

Il est sensible au contexte, tout comme a b(c);deux déclarations et variable d'analyse valides. Lorsque vous dites "si cc'est un type", c'est le contexte, juste là, et vous avez décrit exactement comment C ++ y est sensible. Si vous n'aviez pas ce contexte de "ce qui estc ?" vous ne pouviez pas analyser cela sans ambiguïté.

Ici, le contexte est exprimé dans le choix des jetons - l'analyseur lit un identifiant comme un jeton de nom de type s'il nomme un type. Il s'agit de la résolution la plus simple et évite une grande partie de la complexité d'être sensible au contexte (dans ce cas).

Edit: Il y a, bien sûr, plus de problèmes de sensibilité au contexte, je me suis simplement concentré sur celui que vous avez montré. Les modèles sont particulièrement désagréables pour cela.


1
Aussi a<b<c>>d, non? (Votre exemple est en fait un classique de C , où c'est le seul obstacle à être hors contexte.)
Kerrek SB

C'est plus un problème de lexisme, je pense. Mais est certainement dans la même catégorie, oui.
Puppy

2
L'interrogateur ne demande pas en quoi il est plus contextuel que C, mais seulement pour montrer qu'il est contextuel.
Puppy

Alors .. est C de plus sensible au contexte que C?
Kerrek SB

2
@DeadMG Je ne pense pas que vous répondiez à la question (je ne pense pas que je l'étais non plus). Comment le fait d'avoir des terminaux sur le côté gauche d'une production résout-il ce problème?
user541686

5

Les productions dans la norme C ++ sont écrites sans contexte, mais comme nous le savons tous, ne définissent pas vraiment le langage avec précision. Une partie de ce que la plupart des gens considèrent comme une ambiguïté dans la langue actuelle pourrait (je crois) être résolue sans ambiguïté avec une grammaire contextuelle.

Pour exemple le plus évident, considérons le plus contrariant Parse: int f(X);. Si Xest une valeur, celle-ci se définit fcomme une variable qui sera initialisée avec X. Si Xest un type, il se définit fcomme une fonction prenant un seul paramètre de type X.

En regardant cela d'un point de vue grammatical, nous pourrions le voir comme ceci:

A variable_decl ::= <type> <identifier> '(' initializer ')' ';'

B function_decl ::= <type> <identifier> '(' param_decl ')' ';'

A ::= [declaration of X as value]
B ::= [declaration of X as type]

Bien sûr, pour être tout à fait correct, nous aurions besoin d'ajouter des "trucs" supplémentaires pour tenir compte de la possibilité d'intervenir sur des déclarations d'autres types (c'est-à-dire que A et B devraient tous deux être réellement des "déclarations comprenant la déclaration de X comme ..." , ou quelque chose sur cet ordre).

C'est encore assez différent d'un CSG typique (ou du moins ce que je me souviens d'eux). Cela dépend de la construction d'une table de symboles - la partie qui reconnaît spécifiquementX comme un type ou une valeur, pas seulement un type de déclaration qui précède, mais le type de déclaration correct pour le bon symbole / identificateur.

En tant que tel, je devrais faire quelques recherches pour en être sûr, mais ma supposition immédiate est que cela ne se qualifie pas vraiment en tant que CSG, du moins comme le terme est normalement utilisé.


Les productions (sans contexte) définissent assez bien l'analyse la plus vexante pour qu'elle puisse être analysée par un moteur d'analyse sans contexte. Cela retarde le problème de décider lesquelles des interprétations multiples sont valides jusqu'à la fin de l'analyse, mais cela facilite simplement l'ingénierie de l'analyseur et du résolveur de nom, car ils sont modulaires plutôt qu'enchevêtrés comme dans les analyseurs C ++ conventionnels. Voir AST pour l'analyse la plus vexante: stackoverflow.com/questions/17388771/…
Ira Baxter

5

Le cas le plus simple de la grammaire non contextuelle implique l'analyse des expressions impliquant des modèles.

a<b<c>()

Cela peut analyser comme

template
   |
   a < expr > ()
        |
        <
      /   \
     b     c

Ou

 expr
   |
   <
 /   \
a   template
     |
     b < expr > ()
          |
          c

Les deux AST ne peuvent être désambiguïsés qu'en examinant la déclaration de «a» - l'ancienne AST si «a» est un modèle, ou la seconde sinon.


Je crois que C ++ 11 rend obligatoire cette dernière interprétation, et vous devez ajouter des parens pour vous inscrire à la première.
Joseph Garvin

1
@JosephGarvin, non. Les mandats C ++ qui <doivent être une parenthèse si cela est possible (par exemple, il suit un identifiant qui nomme un modèle). C ++ 11 a ajouté l'exigence selon laquelle >le premier caractère doit >>être interprété comme des crochets si cette utilisation est plausible. Cela affecte l'analyse de l' a<b>c>emplacement d' aun modèle mais n'a aucun effet sur a<b<c>.
rici

@aaron: comment est-ce plus simple que a();(qui est soit expr.callou expr.type.conv)?
rici

@rici: Oups, je ne savais pas que c'était asymétrique.
Joseph Garvin

5
Décrivez-vous l' ambiguïté ou la sensibilité au contexte?
corazza

4

Il a été démontré que les modèles C ++ sont puissants de Turing. Bien qu'il ne s'agisse pas d'une référence officielle, voici un endroit où chercher à cet égard:

http://cpptruths.blogspot.com/2005/11/c-templates-are-turing-complete.html

Je vais tenter une supposition (aussi ancienne qu'une preuve CACM folkorique et concise montrant qu'ALGOL dans les années 60 ne pouvait pas être représenté par un CFG) et dire que C ++ ne peut donc pas être correctement analysé uniquement par un CFG. CFG, en conjonction avec divers mécanismes de TP dans une passe d'arbre ou lors d'événements de réduction - c'est une autre histoire. Dans un sens général, en raison du problème d'arrêt, il existe un programme C ++ qui ne peut pas être démontré comme étant correct / incorrect mais qui est néanmoins correct / incorrect.

{PS- En tant qu'auteur de Meta-S (mentionné par plusieurs personnes ci-dessus) - Je peux très certainement dire que Thothic n'est ni disparu, ni le logiciel disponible gratuitement. J'ai peut-être rédigé cette version de ma réponse de manière à ne pas me faire supprimer ou voter à -3.}


3

C ++ n'est pas sans contexte. Je l'ai appris il y a quelque temps lors d'une conférence sur les compilateurs. Une recherche rapide a donné ce lien, où la section "Syntaxe ou sémantique" explique pourquoi C et C ++ ne sont pas sans contexte:

Wikipédia Talk: Grammaire sans contexte

Cordialement,
Ovanes


2

De toute évidence, si vous prenez la question mot pour mot, presque toutes les langues avec des identificateurs sont sensibles au contexte.

Il faut savoir si un identifiant est un nom de type (un nom de classe, un nom introduit par typedef, un paramètre de modèle de nom de type), un nom de modèle ou un autre nom pour pouvoir correctement utiliser une partie de l'identifiant. Par exemple:

x = (name)(expression);

est un cast si nameest un nom de type et un appel de fonction siname est un nom de fonction. Un autre cas est ce que l'on appelle "l'analyse la plus dérangeante" où il n'est pas possible de différencier la définition de variable et la déclaration de fonction (il existe une règle disant qu'il s'agit d'une déclaration de fonction).

Cette difficulté a introduit le besoin de typenameet templateavec des noms dépendants. Le reste de C ++ n'est pas sensible au contexte pour autant que je sache (c'est-à-dire qu'il est possible d'écrire une grammaire sans contexte pour cela).


2

Meta-S "est un moteur d'analyse contextuelle de Quinn Tyler Jackson. Je ne l'ai pas utilisé, mais il raconte une histoire impressionnante. Consultez ses commentaires dans comp.compilers et consultez rnaparse.com/MetaS%20defined.htm - Ira Baxter 25 juil à 10:42

Le lien correct est l' analyse des énigines

Meta-S était la propriété d'une ancienne société appelée Thothic. Je peux envoyer une copie gratuite du Meta-S à toute personne intéressée et je l'ai utilisée dans la recherche d'analyse d'ARN. Veuillez noter que la "grammaire pseudoknot" incluse dans les dossiers d'exemples a été écrite par un programmeur non bioinformatique et amateur et ne fonctionne pas. Mes grammaires ont une approche différente et fonctionnent assez bien.


C'est en fait une découverte intéressante.
Dervin Thunk

0

Un gros problème ici est que les termes «sans contexte» et «sensible au contexte» sont un peu peu intuitifs en informatique. Pour C ++, la sensibilité au contexte ressemble beaucoup à l'ambiguïté, mais ce n'est pas nécessairement vrai dans le cas général.

En C / ++, une instruction if n'est autorisée qu'à l'intérieur d'un corps de fonction. Cela semblerait le rendre contextuel, non? Et bien non. Les grammaires sans contexte n'ont pas réellement besoin de la propriété où vous pouvez extraire une ligne de code et déterminer si elle est valide. Ce n'est pas vraiment ce que signifie sans contexte. C'est vraiment juste une étiquette qui implique vaguement quelque chose de lié à ce à quoi ça ressemble.

Maintenant, si une instruction dans un corps de fonction est analysée différemment selon quelque chose défini en dehors des ancêtres grammaticaux immédiats (par exemple, si un identifiant décrit un type ou une variable), comme dans le a * b;cas, alors, il est, en fait, contextuel. Il n'y a aucune ambiguïté réelle ici; il sera analysé comme une déclaration d'un pointeur si aest un type et une multiplication sinon.

Être sensible au contexte ne signifie pas nécessairement «difficile à analyser». C n'est en fait pas si difficile car la fameuse a * b;"ambiguïté" peut être résolue avec une table de symboles contenanttypedef s rencontrés précédemment. Il ne nécessite aucune instanciation de modèle arbitraire (qui s'est avéré être Turing Complete) pour résoudre ce cas comme le fait C ++ à l'occasion. Il n'est en fait pas possible d'écrire un programme C qui ne se compilera pas en un temps limité, même s'il a la même sensibilité au contexte que C ++.

Python (et d'autres langages sensibles aux espaces blancs) est également dépendant du contexte, car il nécessite un état dans le lexer pour générer des jetons de retrait et de retrait, mais cela ne rend pas l'analyse plus difficile qu'une grammaire LL-1 typique. Il utilise en fait un générateur d'analyseur syntaxique, ce qui explique pourquoi Python a de tels messages d'erreur de syntaxe non informative. Il est également important de noter ici qu'il n'y a pas "d'ambiguïté" comme le a * b;problème en Python, donnant un bon exemple concret d'un langage contextuel sans grammaire "ambiguë" (comme mentionné dans le premier paragraphe).


-4

Cette réponse dit que C ++ n'est pas sans contexte ... il y a une implication (pas par le répondeur) qu'il ne peut pas être analysé, et la réponse offre un exemple de code difficile qui produit un programme C ++ invalide si une certaine constante n'est pas un nombre premier.

Comme d'autres l'ont observé, la question de savoir si la langue est contextuelle / libre est différente de la même question concernant une grammaire spécifique.

Pour poser la question de la paralysie, je propose des preuves empiriques qu'il existe des grammaires sans contexte pour C ++, qui peuvent être utilisées pour produire un AST pour une analyse sans contexte du texte source en analysant en fait celui-ci avec un GLR existant -un outil basé sur l'analyseur qui est piloté par une grammaire explicite.

Oui, elle réussit en «acceptant trop»; tout ce qu'il accepte n'est pas un programme C ++ valide, c'est pourquoi il est suivi de vérifications supplémentaires (vérifications de type). Et oui, le vérificateur de type peut rencontrer des problèmes de calculabilité. Dans la pratique, les outils n'ont pas ce problème; si les gens écrivaient des programmes comme ça, aucun ne compilerait. (Je pense que la norme limite en fait la quantité de calcul que vous pouvez faire en dépliant un modèle, donc en fait le calcul est en fait fini mais probablement assez gros).

Si vous voulez dire, déterminez si le programme source est membre de l'ensemble des programmes source C ++ valides , je conviendrai que le problème est beaucoup plus difficile. Mais ce n'est pas l' analyse qui est le problème.

L'outil résout ce problème en isolant l'analyse de la vérification de type du programme analysé. (Lorsqu'il y a plusieurs interprétations en l'absence de contexte, il enregistre un nœud d' ambiguïté dans l'arbre d'analyse avec plusieurs analyses possibles; la vérification de type décide laquelle est correcte et élimine les sous-arbres invalides). Vous pouvez voir un arbre d'analyse (partiel) dans l'exemple ci-dessous; l'arbre entier est trop grand pour tenir dans une réponse SO. Notez que vous obtenez un arbre d'analyse, que la valeur 234797 ou 234799 soit utilisée.

L'exécution du résolveur de nom / type de l'outil sur l'AST avec la valeur d'origine 234799 réussit. Avec la valeur 234797, le résolveur de nom échoue (comme prévu) avec le message d'erreur "typen n'est pas un type". et donc cette version n'est pas un programme C ++ valide.

967 tree nodes in tree.
15 ambiguity nodes in tree.
(translation_unit@Cpp~GCC5=2#6b11a20^0 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
 (declaration_seq@Cpp~GCC5=1021#6b06640^1#6b11a20:1 {10} Line 1 Column 1 File C:/temp/prime_with_templates.cpp
  (pp_declaration_seq@Cpp~GCC5=1022#6b049a0^1#6b06640:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   (declaration@Cpp~GCC5=1036#6b04980^1#6b049a0:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   |(template_declaration@Cpp~GCC5=2079#6b04960^1#6b04980:1 Line 1 Column 1 File C:/temp/prime_with_templates.cpp
   | (template_parameter_list@Cpp~GCC5=2082#6afbde0^1#6b04960:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |  (template_parameter@Cpp~GCC5=2085#6afbd80^1#6afbde0:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   (parameter_declaration@Cpp~GCC5=1611#6afbd40^1#6afbd80:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6afb880^1#6afbd40:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6afb840^1#6afb880:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |  (trailing_type_specifier@Cpp~GCC5=1118#6afb7e0^1#6afb840:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   (simple_type_specifier@Cpp~GCC5=1138#6afb7a0^1#6afb7e0:1 Line 1 Column 10 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |  )trailing_type_specifier#6afb7e0
   |   | )decl_specifier#6afb840
   |   |)basic_decl_specifier_seq#6afb880
   |   |(ptr_declarator@Cpp~GCC5=1417#6afbc40^1#6afbd40:2 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   | (noptr_declarator@Cpp~GCC5=1421#6afbba0^1#6afbc40:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |  (declarator_id@Cpp~GCC5=1487#6afbb80^1#6afbba0:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   (id_expression@Cpp~GCC5=317#6afbaa0^1#6afbb80:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |(unqualified_id@Cpp~GCC5=319#6afb9c0^1#6afbaa0:1 Line 1 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6afb780^1#6afb9c0:1[`V'] Line 1 Column 15 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |)unqualified_id#6afb9c0
   |   |   )id_expression#6afbaa0
   |   |  )declarator_id#6afbb80
   |   | )noptr_declarator#6afbba0
   |   |)ptr_declarator#6afbc40
   |   )parameter_declaration#6afbd40
   |  )template_parameter#6afbd80
   | )template_parameter_list#6afbde0
   | (declaration@Cpp~GCC5=1033#6b04940^1#6b04960:2 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |  (block_declaration@Cpp~GCC5=1050#6b04920^1#6b04940:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   (simple_declaration@Cpp~GCC5=1060#6b04900^1#6b04920:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6b048e0^1#6b04900:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6b048c0^1#6b048e0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |  (type_specifier@Cpp~GCC5=1110#6b048a0^1#6b048c0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   (class_specifier@Cpp~GCC5=1761#6b04880^1#6b048a0:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   |(class_head@Cpp~GCC5=1763#6afb980^1#6b04880:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp
   |   |   | (class_key@Cpp~GCC5=1791#6afbca0^1#6afb980:1 Line 1 Column 18 File C:/temp/prime_with_templates.cpp)class_key
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6afbcc0^1#6afb980:2[`answer'] Line 1 Column 25 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   | (optional_base_clause@Cpp~GCC5=1872#6afba60^1#6afb980:3 Line 1 Column 32 File C:/temp/prime_with_templates.cpp)optional_base_clause
   |   |   |)class_head#6afb980
   |   |   |(member_specification@Cpp~GCC5=1794#6b042e0^1#6b04880:2 {2} Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   | (member_declaration_or_access_specifier@Cpp~GCC5=1806#6b04060^1#6b042e0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |  (member_declaration@Cpp~GCC5=1822#6b04040^1#6b04060:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   (function_definition@Cpp~GCC5=1632#6b04020^1#6b04040:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |(function_head@Cpp~GCC5=1673#6afbec0^1#6b04020:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   | (ptr_declarator@Cpp~GCC5=1417#6afbfe0^1#6afbec0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (noptr_declarator@Cpp~GCC5=1422#6afbf80^1#6afbfe0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (noptr_declarator@Cpp~GCC5=1421#6afbf60^1#6afbf80:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(declarator_id@Cpp~GCC5=1487#6afbea0^1#6afbf60:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (id_expression@Cpp~GCC5=317#6afbb40^1#6afbea0:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (unqualified_id@Cpp~GCC5=319#6afbc80^1#6afbb40:1 Line 1 Column 34 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6afbc20^1#6afbc80:1[`answer'] Line 1 Column 34 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   |   |  )unqualified_id#6afbc80
   |   |   |   |   | )id_expression#6afbb40
   |   |   |   |   |)declarator_id#6afbea0
   |   |   |   |   )noptr_declarator#6afbf60
   |   |   |   |   (parameter_declaration_clause@Cpp~GCC5=1559#6afbd00^1#6afbf80:2 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(pp_parameter_declaration_list@Cpp~GCC5=1570#6afb940^1#6afbd00:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (pp_parameter_declaration_seq@Cpp~GCC5=1574#6afb800^1#6afb940:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (parameter_declaration@Cpp~GCC5=1610#6afb9a0^1#6afb800:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (basic_decl_specifier_seq@Cpp~GCC5=1070#6afbf40^1#6afb9a0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(decl_specifier@Cpp~GCC5=1073#6afbfa0^1#6afbf40:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   | (trailing_type_specifier@Cpp~GCC5=1118#6afbfc0^1#6afbfa0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |  (simple_type_specifier@Cpp~GCC5=1140#6afb860^1#6afbfc0:1 Line 1 Column 41 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |   |   |   | )trailing_type_specifier#6afbfc0
   |   |   |   |   |   |)decl_specifier#6afbfa0
   |   |   |   |   |   )basic_decl_specifier_seq#6afbf40
   |   |   |   |   |  )parameter_declaration#6afb9a0
   |   |   |   |   | )pp_parameter_declaration_seq#6afb800
   |   |   |   |   |)pp_parameter_declaration_list#6afb940
   |   |   |   |   )parameter_declaration_clause#6afbd00
   |   |   |   |   (function_qualifiers@Cpp~GCC5=1438#6afbce0^1#6afbf80:3 Line 1 Column 46 File C:/temp/prime_with_templates.cpp)function_qualifiers
   |   |   |   |  )noptr_declarator#6afbf80
   |   |   |   | )ptr_declarator#6afbfe0
   |   |   |   |)function_head#6afbec0
   |   |   |   |(function_body@Cpp~GCC5=1680#6b04000^1#6b04020:2 Line 1 Column 46 File C:/temp/prime_with_templates.cpp
   |   |   |   | (compound_statement@Cpp~GCC5=888#6afbee0^1#6b04000:1 Line 1 Column 46 File C:/temp/prime_with_templates.cpp)compound_statement
   |   |   |   |)function_body#6b04000
   |   |   |   )function_definition#6b04020
   |   |   |  )member_declaration#6b04040
   |   |   | )member_declaration_or_access_specifier#6b04060
   |   |   | (member_declaration_or_access_specifier@Cpp~GCC5=1806#6b042c0^1#6b042e0:2 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |  (member_declaration@Cpp~GCC5=1822#6b04820^1#6b042c0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   (function_definition@Cpp~GCC5=1632#6b04280^1#6b04820:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |(function_head@Cpp~GCC5=1674#6b04220^1#6b04280:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   | (basic_decl_specifier_seq@Cpp~GCC5=1070#6b040e0^1#6b04220:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (decl_specifier@Cpp~GCC5=1073#6b040c0^1#6b040e0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (trailing_type_specifier@Cpp~GCC5=1118#6b040a0^1#6b040c0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(simple_type_specifier@Cpp~GCC5=1138#6b04080^1#6b040a0:1 Line 1 Column 49 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |   |   )trailing_type_specifier#6b040a0
   |   |   |   |  )decl_specifier#6b040c0
   |   |   |   | )basic_decl_specifier_seq#6b040e0
   |   |   |   | (ptr_declarator@Cpp~GCC5=1417#6b04200^1#6b04220:2 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (noptr_declarator@Cpp~GCC5=1422#6b041e0^1#6b04200:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (noptr_declarator@Cpp~GCC5=1421#6b041a0^1#6b041e0:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(declarator_id@Cpp~GCC5=1487#6b04180^1#6b041a0:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (id_expression@Cpp~GCC5=317#6b04160^1#6b04180:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (unqualified_id@Cpp~GCC5=320#6b04140^1#6b04160:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (operator_function_id@Cpp~GCC5=2027#6b04120^1#6b04140:1 Line 1 Column 54 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(operator@Cpp~GCC5=2070#6b04100^1#6b04120:1 Line 1 Column 62 File C:/temp/prime_with_templates.cpp)operator
   |   |   |   |   |   )operator_function_id#6b04120
   |   |   |   |   |  )unqualified_id#6b04140
   |   |   |   |   | )id_expression#6b04160
   |   |   |   |   |)declarator_id#6b04180
   |   |   |   |   )noptr_declarator#6b041a0
   |   |   |   |   (parameter_declaration_clause@Cpp~GCC5=1558#6afba40^1#6b041e0:2 Line 1 Column 65 File C:/temp/prime_with_templates.cpp)parameter_declaration_clause
   |   |   |   |   (function_qualifiers@Cpp~GCC5=1438#6b041c0^1#6b041e0:3 Line 1 Column 66 File C:/temp/prime_with_templates.cpp)function_qualifiers
   |   |   |   |  )noptr_declarator#6b041e0
   |   |   |   | )ptr_declarator#6b04200
   |   |   |   |)function_head#6b04220
   |   |   |   |(function_body@Cpp~GCC5=1680#6b04300^1#6b04280:2 Line 1 Column 66 File C:/temp/prime_with_templates.cpp
   |   |   |   | (compound_statement@Cpp~GCC5=889#6b04760^1#6b04300:1 Line 1 Column 66 File C:/temp/prime_with_templates.cpp
   |   |   |   |  (pp_statement_seq@Cpp~GCC5=894#6b04780^1#6b04760:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   (statement@Cpp~GCC5=857#6b04440^1#6b04780:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |(jump_statement@Cpp~GCC5=1011#6afba80^1#6b04440:1 Line 1 Column 67 File C:/temp/prime_with_templates.cpp
   |   |   |   |   | (pm_expression@Cpp~GCC5=551#6b04380^1#6afba80:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |  (cast_expression@Cpp~GCC5=543#6b04360^1#6b04380:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   (unary_expression@Cpp~GCC5=465#6b04340^1#6b04360:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |(primary_expression@Cpp~GCC5=307#6b04320^1#6b04340:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   | (id_expression@Cpp~GCC5=317#6b042a0^1#6b04320:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |  (unqualified_id@Cpp~GCC5=319#6b04260^1#6b042a0:1 Line 1 Column 74 File C:/temp/prime_with_templates.cpp
   |   |   |   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6b04240^1#6b04260:1[`V'] Line 1 Column 74 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   |   |   |  )unqualified_id#6b04260
   |   |   |   |   |   | )id_expression#6b042a0
   |   |   |   |   |   |)primary_expression#6b04320
   |   |   |   |   |   )unary_expression#6b04340
   |   |   |   |   |  )cast_expression#6b04360
   |   |   |   |   | )pm_expression#6b04380
   |   |   |   |   |)jump_statement#6afba80
   |   |   |   |   )statement#6b04440
   |   |   |   |  )pp_statement_seq#6b04780
   |   |   |   | )compound_statement#6b04760
   |   |   |   |)function_body#6b04300
   |   |   |   )function_definition#6b04280
   |   |   |  )member_declaration#6b04820
   |   |   | )member_declaration_or_access_specifier#6b042c0
   |   |   |)member_specification#6b042e0
   |   |   )class_specifier#6b04880
   |   |  )type_specifier#6b048a0
   |   | )decl_specifier#6b048c0
   |   |)basic_decl_specifier_seq#6b048e0
   |   )simple_declaration#6b04900
   |  )block_declaration#6b04920
   | )declaration#6b04940
   |)template_declaration#6b04960
   )declaration#6b04980
  )pp_declaration_seq#6b049a0
  (pp_declaration_seq@Cpp~GCC5=1022#6b06620^1#6b06640:2 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   (declaration@Cpp~GCC5=1036#6b06600^1#6b06620:1 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   |(template_declaration@Cpp~GCC5=2079#6b065e0^1#6b06600:1 Line 3 Column 1 File C:/temp/prime_with_templates.cpp
   | (template_parameter_list@Cpp~GCC5=2083#6b05460^1#6b065e0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |  (template_parameter_list@Cpp~GCC5=2083#6b05140^1#6b05460:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   (template_parameter_list@Cpp~GCC5=2083#6b04ee0^1#6b05140:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |(template_parameter_list@Cpp~GCC5=2082#6b04cc0^1#6b04ee0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   | (template_parameter@Cpp~GCC5=2085#6b04ca0^1#6b04cc0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |  (parameter_declaration@Cpp~GCC5=1611#6b04c80^1#6b04ca0:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04a40^1#6b04c80:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   |(decl_specifier@Cpp~GCC5=1073#6b04a20^1#6b04a40:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   | (trailing_type_specifier@Cpp~GCC5=1118#6b04a00^1#6b04a20:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp
   |   |   |  (simple_type_specifier@Cpp~GCC5=1138#6b049e0^1#6b04a00:1 Line 3 Column 10 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   | )trailing_type_specifier#6b04a00
   |   |   |)decl_specifier#6b04a20
   |   |   )basic_decl_specifier_seq#6b04a40
   |   |   (ptr_declarator@Cpp~GCC5=1417#6b04c40^1#6b04c80:2 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |(noptr_declarator@Cpp~GCC5=1421#6b04be0^1#6b04c40:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   | (declarator_id@Cpp~GCC5=1487#6b04bc0^1#6b04be0:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |  (id_expression@Cpp~GCC5=317#6b04b60^1#6b04bc0:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |   (unqualified_id@Cpp~GCC5=319#6b04ac0^1#6b04b60:1 Line 3 Column 15 File C:/temp/prime_with_templates.cpp
   |   |   |   |(IDENTIFIER@Cpp~GCC5=3368#6b049c0^1#6b04ac0:1[`no'] Line 3 Column 15 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |   )unqualified_id#6b04ac0
   |   |   |  )id_expression#6b04b60
   |   |   | )declarator_id#6b04bc0
   |   |   |)noptr_declarator#6b04be0
   |   |   )ptr_declarator#6b04c40
   |   |  )parameter_declaration#6b04c80
   |   | )template_parameter#6b04ca0
   |   |)template_parameter_list#6b04cc0
   |   |(template_parameter@Cpp~GCC5=2085#6b04ec0^1#6b04ee0:2 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   | (parameter_declaration@Cpp~GCC5=1611#6b04ea0^1#6b04ec0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |  (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04b40^1#6b04ea0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   (decl_specifier@Cpp~GCC5=1073#6b04ba0^1#6b04b40:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   |(trailing_type_specifier@Cpp~GCC5=1118#6b04c60^1#6b04ba0:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp
   |   |   | (simple_type_specifier@Cpp~GCC5=1138#6b04580^1#6b04c60:1 Line 3 Column 19 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   |)trailing_type_specifier#6b04c60
   |   |   )decl_specifier#6b04ba0
   |   |  )basic_decl_specifier_seq#6b04b40
   |   |  (ptr_declarator@Cpp~GCC5=1417#6b04e60^1#6b04ea0:2 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   (noptr_declarator@Cpp~GCC5=1421#6b04e40^1#6b04e60:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |(declarator_id@Cpp~GCC5=1487#6b04de0^1#6b04e40:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   | (id_expression@Cpp~GCC5=317#6b04d80^1#6b04de0:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |  (unqualified_id@Cpp~GCC5=319#6b04ce0^1#6b04d80:1 Line 3 Column 24 File C:/temp/prime_with_templates.cpp
   |   |   |   (IDENTIFIER@Cpp~GCC5=3368#6b04560^1#6b04ce0:1[`yes'] Line 3 Column 24 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |  )unqualified_id#6b04ce0
   |   |   | )id_expression#6b04d80
   |   |   |)declarator_id#6b04de0
   |   |   )noptr_declarator#6b04e40
   |   |  )ptr_declarator#6b04e60
   |   | )parameter_declaration#6b04ea0
   |   |)template_parameter#6b04ec0
   |   )template_parameter_list#6b04ee0
   |   (template_parameter@Cpp~GCC5=2085#6b05120^1#6b05140:2 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |(parameter_declaration@Cpp~GCC5=1611#6b05100^1#6b05120:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   | (basic_decl_specifier_seq@Cpp~GCC5=1070#6b04d20^1#6b05100:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |  (decl_specifier@Cpp~GCC5=1073#6b04dc0^1#6b04d20:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |   (trailing_type_specifier@Cpp~GCC5=1118#6b04e80^1#6b04dc0:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp
   |   |   |(simple_type_specifier@Cpp~GCC5=1140#6b046e0^1#6b04e80:1 Line 3 Column 29 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |   )trailing_type_specifier#6b04e80
   |   |  )decl_specifier#6b04dc0
   |   | )basic_decl_specifier_seq#6b04d20
   |   | (ptr_declarator@Cpp~GCC5=1417#6b05080^1#6b05100:2 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |  (noptr_declarator@Cpp~GCC5=1421#6b05020^1#6b05080:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   (declarator_id@Cpp~GCC5=1487#6b05000^1#6b05020:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   |(id_expression@Cpp~GCC5=317#6b04fa0^1#6b05000:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   | (unqualified_id@Cpp~GCC5=319#6b04f00^1#6b04fa0:1 Line 3 Column 33 File C:/temp/prime_with_templates.cpp
   |   |   |  (IDENTIFIER@Cpp~GCC5=3368#6b046c0^1#6b04f00:1[`f'] Line 3 Column 33 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   | )unqualified_id#6b04f00
   |   |   |)id_expression#6b04fa0
   |   |   )declarator_id#6b05000
   |   |  )noptr_declarator#6b05020
   |   | )ptr_declarator#6b05080
   |   |)parameter_declaration#6b05100
   |   )template_parameter#6b05120
   |  )template_parameter_list#6b05140
   |  (template_parameter@Cpp~GCC5=2085#6b05440^1#6b05460:2 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   (parameter_declaration@Cpp~GCC5=1611#6b05420^1#6b05440:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |(basic_decl_specifier_seq@Cpp~GCC5=1070#6b05160^1#6b05420:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   | (decl_specifier@Cpp~GCC5=1073#6b04fe0^1#6b05160:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |  (trailing_type_specifier@Cpp~GCC5=1118#6b050e0^1#6b04fe0:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp
   |   |   (simple_type_specifier@Cpp~GCC5=1140#6b050c0^1#6b050e0:1 Line 3 Column 36 File C:/temp/prime_with_templates.cpp)simple_type_specifier
   |   |  )trailing_type_specifier#6b050e0
   |   | )decl_specifier#6b04fe0
   |   |)basic_decl_specifier_seq#6b05160
   |   |(ptr_declarator@Cpp~GCC5=1417#6b053e0^1#6b05420:2 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   | (noptr_declarator@Cpp~GCC5=1421#6b053c0^1#6b053e0:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |  (declarator_id@Cpp~GCC5=1487#6b05360^1#6b053c0:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   (id_expression@Cpp~GCC5=317#6b05280^1#6b05360:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   |(unqualified_id@Cpp~GCC5=319#6b051a0^1#6b05280:1 Line 3 Column 40 File C:/temp/prime_with_templates.cpp
   |   |   | (IDENTIFIER@Cpp~GCC5=3368#6b046a0^1#6b051a0:1[`p'] Line 3 Column 40 File C:/temp/prime_with_templates.cpp)IDENTIFIER
   |   |   |)unqualified_id#6b051a0
   |   |   )id_expression#6b05280
   |   |  )declarator_id#6b05360
   |   | )noptr_declarator#6b053c0
   |   |)ptr_declarator#6b053e0
   |   )parameter_declaration#6b05420
   |  )template_parameter#6b05440
   | )template_parameter_list#6b05460

Déterminer s'il s'agit d'une déclaration de variable ou d'une multiplication n'est pas une fonction de vérification de type. Je devais aussi frotter votre réponse sur ce truc d'auto-promotion ... encore une fois.
Puppy

@Puppy: vous pouvez dire ce que vous aimez, mais c'est ainsi que l'outil fonctionne. La suppression du nom de l'outil va probablement inciter les gens à demander quel est le nom de l'outil.
Ira Baxter

Que ce soit ou non comment l'outil fonctionne n'a pas d'importance, car la question ne demande pas le fonctionnement de l'outil. De plus, je pense que nous pouvons attendre en toute sécurité que cela se produise réellement.
Puppy
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.