¿Es C ++ libre de contexto o sensible al contexto?

405

A menudo escucho afirmaciones de que C ++ es un lenguaje sensible al contexto. Tome el siguiente ejemplo:

a b(c);

¿Es esta una definición variable o una declaración de función? Eso depende del significado del símbolo c. Si ces una variable , a b(c);define una variable llamada bde tipo a. Se inicializa directamente con c. Pero si ces un tipo , a b(c);declara una función llamada bque toma un cy devuelve un a.

Si busca la definición de lenguajes sin contexto, básicamente le dirá que todas las reglas gramaticales deben tener lados izquierdos que consisten en exactamente un símbolo no terminal. Las gramáticas sensibles al contexto, por otro lado, permiten cadenas arbitrarias de símbolos terminales y no terminales en el lado izquierdo.

Navegando por el Apéndice A de "El lenguaje de programación C ++", no pude encontrar una sola regla gramatical que tuviera algo más que un solo símbolo no terminal en su lado izquierdo. Eso implicaría que C ++ no tiene contexto. (Por supuesto, cada lenguaje libre de contexto también es sensible al contexto en el sentido de que los lenguajes libres de contexto forman un subconjunto de los lenguajes sensibles al contexto, pero ese no es el punto).

Entonces, ¿C ++ no tiene contexto o es sensible al contexto?

flujo libre
fuente
12
@CarlNorum Por favor, muéstrame una sola regla gramatical de C ++ que no consista en un solo símbolo no terminal en su lado izquierdo y te creeré de inmediato.
fredoverflow
99
IIUC depende un poco de dónde dibuja la línea para la sensibilidad al contexto. Creo que he visto a personas argumentar que casi todos los lenguajes de programación tipados estáticamente son sensibles al contexto, no porque no se pueda construir un compilador práctico para ellos con las herramientas de análisis CFG, sino porque tales implementaciones "engañan" al analizar algunos programas no válidos y solo los rechaza más tarde, durante la verificación de tipo Entonces, si considera que los programas mal escritos no están en el lenguaje (en el sentido CS, es decir, un conjunto de cadenas) que el analizador debería aceptar, más lenguajes que C ++ son sensibles al contexto.
66
@DeadMG: No, estás equivocado. No hay "análisis" o "semántica" en la teoría del lenguaje formal, solo "lenguaje", que es un conjunto de cadenas.
jpalecek
27
Hasta el momento, ninguna respuesta ha abordado su definición de "gramática libre de contexto". En mi opinión, la respuesta correcta a esta pregunta cita una producción en el apéndice A que no se ajusta a su definición o demuestra que su definición es incorrecta o insuficiente. ¡Defender su posición!
ligereza corre en órbita el
8
Ver ¿Es la gramática de D realmente libre de contexto? . De hecho, ¡creo que todos aquí deberían leer esa pregunta y sus respuestas!
ligereza corre en órbita el

Respuestas:

341

A continuación está mi demostración favorita (actual) de por qué analizar C ++ es (probablemente) Turing completo , ya que muestra un programa que es sintácticamente correcto si y solo si un entero dado es primo.

Por lo tanto, afirmo que C ++ no es libre de contexto ni sensible al contexto .

Si permite secuencias de símbolos arbitrarias en ambos lados de cualquier producción, produce una gramática de tipo 0 ("sin restricciones") en la jerarquía de Chomsky , que es más poderosa que una gramática sensible al contexto; las gramáticas sin restricciones son completas de Turing. Una gramática sensible al contexto (Tipo-1) permite múltiples símbolos de contexto en el lado izquierdo de una producción, pero el mismo contexto debe aparecer en el lado derecho de la producción (de ahí el nombre "sensible al contexto"). [1] Las gramáticas sensibles al contexto son equivalentes a las máquinas de Turing con límites lineales .

En el programa de ejemplo, el cálculo principal podría ser realizado por una máquina de Turing con límites lineales, por lo que no prueba la equivalencia de Turing, pero la parte importante es que el analizador debe realizar el cálculo para realizar un análisis sintáctico. Podría haber sido cualquier cálculo expresable como una creación de instancias de plantilla y hay muchas razones para creer que la creación de instancias de plantilla C ++ es completa de Turing. Ver, por ejemplo, el artículo de 2003 de Todd L. Veldhuizen .

De todos modos, C ++ puede ser analizado por una computadora, por lo que ciertamente podría ser analizado por una máquina Turing. En consecuencia, una gramática sin restricciones podría reconocerlo. En realidad, escribir tal gramática no sería práctico, por lo que el estándar no intenta hacerlo. (Vea abajo.)

El problema con la "ambigüedad" de ciertas expresiones es principalmente una pista falsa. Para empezar, la ambigüedad es una característica de una gramática particular, no un lenguaje. Incluso si se puede demostrar que un idioma no tiene gramáticas inequívocas, si puede ser reconocido por una gramática libre de contexto, es libre de contexto. Del mismo modo, si no puede ser reconocido por una gramática libre de contexto pero puede ser reconocido por una gramática sensible al contexto, es sensible al contexto. La ambigüedad no es relevante.

Pero en cualquier caso, como la línea 21 (es decir auto b = foo<IsPrime<234799>>::typen<1>();) en el programa a continuación, las expresiones no son ambiguas en absoluto; simplemente se analizan de manera diferente según el contexto. En la expresión más simple del problema, la categoría sintáctica de ciertos identificadores depende de cómo se hayan declarado (tipos y funciones, por ejemplo), lo que significa que el lenguaje formal debería reconocer el hecho de que dos cadenas de longitud arbitraria en Los mismos programas son idénticos (declaración y uso). Esto puede ser modelado por la gramática de "copia", que es la gramática que reconoce dos copias exactas consecutivas de la misma palabra. Es fácil de probar con el lema de bombeoque este lenguaje no está libre de contexto. Es posible una gramática sensible al contexto para este idioma, y ​​se proporciona una gramática de tipo 0 en la respuesta a esta pregunta: /math/163830/context-sensitive-grammar-for-the- lenguaje de copia .

Si uno intentara escribir una gramática sensible al contexto (o sin restricciones) para analizar C ++, posiblemente llenaría el universo con garabatos. Escribir una máquina de Turing para analizar C ++ sería una tarea igualmente imposible. Incluso escribir un programa en C ++ es difícil, y que yo sepa, ninguno ha demostrado ser correcto. Esta es la razón por la cual el estándar no intenta proporcionar una gramática formal completa, y por qué elige escribir algunas de las reglas de análisis en inglés técnico.

Lo que parece una gramática formal en el estándar C ++ no es la definición formal completa de la sintaxis del lenguaje C ++. Ni siquiera es la definición formal completa del lenguaje después del preprocesamiento, lo que podría ser más fácil de formalizar. (Sin embargo, ese no sería el lenguaje: el lenguaje C ++ definido por el estándar incluye el preprocesador, y la operación del preprocesador se describe algorítmicamente, ya que sería extremadamente difícil de describir en cualquier formalismo gramatical. Está en esa sección del estándar donde se describe la descomposición léxica, incluidas las reglas en las que debe aplicarse más de una vez).

Las diversas gramáticas (dos gramáticas superpuestas para el análisis léxico, una que tiene lugar antes del preprocesamiento y la otra, si es necesario, después, más la gramática "sintáctica") se recogen en el Apéndice A, con esta nota importante (énfasis agregado):

Este resumen de la sintaxis de C ++ pretende ser una ayuda para la comprensión. No es una declaración exacta del idioma . En particular, la gramática descrita aquí acepta un superconjunto de construcciones válidas de C ++ . Las reglas de desambiguación (6.8, 7.1, 10.2) deben aplicarse para distinguir expresiones de declaraciones. Además, las reglas de control de acceso, ambigüedad y tipo deben usarse para eliminar construcciones sintácticamente válidas pero sin sentido.

Finalmente, aquí está el programa prometido. La línea 21 es sintácticamente correcta si y solo si la N IsPrime<N>es primo. De lo contrario, typenes un entero, no una plantilla, por lo que typen<1>()se analiza como (typen<1)>()sintácticamente incorrecto porque ()no es una expresión sintácticamente válida.

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] Para decirlo más técnicamente, cada producción en una gramática sensible al contexto debe tener la forma:

αAβ → αγβ

donde Aes un no terminal y α, βposiblemente son secuencias vacías de símbolos gramaticales, y γes una secuencia no vacía. (Los símbolos gramaticales pueden ser terminales o no terminales).

Esto se puede leer A → γsolo en el contexto [α, β]. En una gramática libre de contexto (Tipo 2), αy βdebe estar vacía.

Resulta que también puede restringir las gramáticas con la restricción "monotónica", donde cada producción debe tener la forma:

α → βdonde |α| ≥ |β| > 0  ( |α|significa "la longitud de α")

Es posible demostrar que el conjunto de idiomas reconocidos por las gramáticas monotónicas es exactamente el mismo que el conjunto de idiomas reconocidos por las gramáticas sensibles al contexto, y a menudo es más fácil basar las pruebas en las gramáticas monotónicas. En consecuencia, es bastante común ver "sensible al contexto" usado como si significara "monótono".

rici
fuente
27
Entonces, no solo es sensible al contexto, sino que se puede hacer que dependa de cualquier contexto que pueda expresar en plantillas, que son completas de Turing.
Cachorro
77
@mehrdad, el OP dice "lenguaje sensible al contexto", no gramática sensible al contexto. La ambigüedad es una característica de una gramática, no un lenguaje. El lenguaje es de hecho sensible al contexto, pero no porque una gramática particular para él sea ambigua.
rici
2
Tenga en cuenta que mi ejemplo no es ambiguo. Es una expresión inequívoca de un programa válido. Si cambia el valor en la línea 21, puede estar mal formado. Pero en ninguno de los casos es ambiguo.
rici
55
Tengo una duda: como muestra, el resultado de la evaluación de la plantilla puede marcar la diferencia entre un programa bien formado y uno mal formado. La evaluación de la plantilla está completa. Entonces, ¿no sería correcto determinar si una cadena está en el lenguaje (C ++) requiere turing-completeness? Como usted dice, un lenguaje sensible al contexto es "solo" un "autómata lineal lineal", que no es AFAIK completo. ¿O su argumento hace uso de los límites que el estándar C ++ pone en algunas cosas, incluida la profundidad de evaluación de plantilla?
44
@AntonGolov: Mi versión original de ese ejemplo hizo exactamente eso (puedes lograrlo poniéndolo 0dentro (), por uno simple), pero creo que es más interesante de esta manera, porque demuestra que necesitas la creación de instancias de plantilla incluso para reconocer si una cadena es un programa C ++ sintácticamente correcto. Si ambas ramas se compilan, entonces tendría que trabajar más para refutar el argumento de que la diferencia es "semántica". Curiosamente, aunque a menudo me desafían a definir "sintáctico", nadie ha ofrecido una definición de "semántico" que no sea "cosas que no creo que sean sintácticas" :)
rici
115

Primero, usted observó correctamente que no hay reglas sensibles al contexto en la gramática al final del estándar C ++, por lo que la gramática está libre de contexto.

Sin embargo, esa gramática no describe con precisión el lenguaje C ++, porque produce programas que no son C ++ como

int m() { m++; }

o

typedef static int int;

El lenguaje C ++ definido como "el conjunto de programas C ++ bien formados" no está libre de contexto (es posible demostrar que las variables simplemente exigentes que se declaran lo hacen así). Dado que teóricamente puedes escribir programas completos de Turing en plantillas y hacer que un programa esté mal formado en función de su resultado, ni siquiera es sensible al contexto.

Ahora, las personas (ignorantes) (generalmente no teóricos del lenguaje, sino diseñadores de analizadores sintácticos) generalmente usan "no sin contexto" en algunos de los siguientes significados

  • ambiguo
  • no se puede analizar con Bison
  • no LL (k), LR (k), LALR (k) o cualquier clase de lenguaje definido por analizador que elijan

La gramática en la parte posterior del estándar no satisface estas categorías (es decir, es ambigua, no LL (k) ...) por lo que la gramática de C ++ "no está libre de contexto" para ellos. Y en cierto sentido, tienen razón, es muy difícil producir un analizador de C ++ que funcione.

Tenga en cuenta que las propiedades aquí utilizadas solo están débilmente conectadas a lenguajes libres de contexto: la ambigüedad no tiene nada que ver con la sensibilidad al contexto (de hecho, las reglas sensibles al contexto generalmente ayudan a desambiguar las producciones), los otros dos son simplemente subconjuntos de contexto -Los idiomas libres. Y analizar lenguajes libres de contexto no es un proceso lineal (aunque sí lo es).

jpalecek
fuente
77
ambiguity doesn't have anything to do with context-sensitivityEsta también era mi intuición, así que me alegra ver que alguien (a) está de acuerdo y (b) lo explica donde no pude. Creo que descalifica todos los argumentos en los que se basa a b(c);y satisface parcialmente la pregunta original cuya premisa eran afirmaciones "a menudo escuchadas" de que la sensibilidad al contexto se debe a la ambigüedad ... especialmente cuando para la gramática en realidad no hay ambigüedad incluso en el MVP
ligereza corre en órbita el
66
@KonradRudolph: Lo que dice el estándar es "Hay una cantidad definida por la implementación que especifica el límite en la profundidad total de las instancias recursivas, que podría involucrar más de una plantilla. El resultado de una recursión infinita en la instanciación no está definido". (14.7.1p15) Interpreto que significa que no se requiere una implementación para comprender todos los programas válidos de c ++, no que los programas con una profundidad de recursión demasiado grande no sean válidos. Los únicos que están marcados como no válidos son aquellos con una profundidad de recursión infinita.
rici
3
@KonradRudolph: Disputo que sea "referencia general". El hecho de que leí ese artículo bastante complejo y no lo comprendo lo suficiente como para descifrar este pequeño hecho debería ser suficiente para demostrarlo. No es como si dijeras algo como "las computadoras comúnmente usan electricidad", o "los bits pueden ser verdaderos o falsos".
Carreras de ligereza en órbita el
3
Si este hecho es realmente tan conocido, creo que sería mucho más fácil encontrar una referencia a él que discutir extensamente sobre si se debe proporcionar o no. Sin mencionar constructivo.
Samuel Edwin Ward
55
Por lo que puedo decir, @Konrad se equivocó cuando dijo "El contexto sensible es equivalente a Turing completo". (al menos, lo era si denotaba "recursivamente enumerable" por "Turing completo"), y desde entonces no ha podido reconocer este error. Aquí hay una referencia para la relación de inclusión de conjunto adecuada involucrada aquí: en.wikipedia.org/wiki/Chomsky_hierarchy
pnkfelix
61

Si. La siguiente expresión tiene un orden diferente de operaciones según el tipo de contexto resuelto :

Editar: cuando el orden real de operación varía, hace que sea increíblemente difícil usar un compilador "normal" que analiza un AST sin decorar antes de decorarlo (propagar información de tipo). Otras cosas sensibles al contexto mencionadas son "bastante fáciles" en comparación con esto (no es que la evaluación de la plantilla sea fácil).

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

Seguido por:

static int foobar( foo < 2 ? 1 < 1 : 0 > & bar );
Sam Harwell
fuente
¿Por qué ese problema no puede resolverse como en C, recordando qué definiciones de tipos están dentro del alcance?
Blaisorblade
1
@Blaisorblade: Una forma de hacer que un compilador sea "limpio" es separar las tareas en pasos independientes en una cadena, como crear un árbol de análisis a partir de la entrada seguido de un paso que realiza el análisis de tipo. C ++ lo obliga a 1) fusionar estos pasos en uno o 2) analizar el documento de acuerdo con ambas / todas las interpretaciones posibles, y permitir que las etapas de resolución de tipo lo reduzcan a la interpretación correcta.
Sam Harwell
@ 280Z28: de acuerdo, pero ese también es el caso de C; Creo que una buena respuesta a esta pregunta debería mostrar por qué C ++ es peor que C. La tesis doctoral vinculada aquí hace eso: stackoverflow.com/a/243447/53974
Blaisorblade
26

Para responder a su pregunta, necesita distinguir dos preguntas diferentes.

  1. La mera sintaxis de casi todos los lenguajes de programación no tiene contexto. Por lo general, se da como una forma extendida de Backus-Naur o gramática libre de contexto.

  2. Sin embargo, incluso si un programa se ajusta a la gramática libre de contexto definida por el lenguaje de programación, no es necesariamente un programa válido . Hay muchas propiedades no libres de contexto que un programa tiene que satisfacer para ser un programa válido. Por ejemplo, la propiedad más simple es el alcance de las variables.

Para concluir, si C ++ está libre de contexto depende de la pregunta que haga.

Dan
fuente
55
Es interesante observar que a menudo tiene que colocar el nivel de "mera sintaxis" más bajo de lo que esperaba, para obtener un CFG para su lenguaje de programación. Tome C, por ejemplo. Puede pensar que la regla gramatical para una declaración de variable simple en C sería VARDECL : TYPENAME IDENTIFIER, pero no puede tener eso, porque no puede distinguir los nombres de tipo de otros identificadores a nivel de CF. Otro ejemplo: a nivel CF, no puede decidir si analizar a*bcomo una declaración variable ( bde tipo puntero a a) o como una multiplicación.
LaC
2
@LaC: Sí, ¡gracias por señalar esto! Por cierto, estoy seguro de que hay un término técnico más comúnmente usado para mera sintaxis . ¿Alguien el término correcto?
Dan
44
@Dan: de lo que estás hablando es de una aproximación del lenguaje dado por una gramática libre de contexto. Por supuesto, tal aproximación es libre de texto por definición. Este es el sentido en el que la "sintaxis" se usa a menudo cuando se habla de lenguajes de programación.
reinierpost
13

Es posible que desee echar un vistazo a The Design & Evolution of C ++ , de Bjarne Stroustrup. En él, describe sus problemas al tratar de usar yacc (o similar) para analizar una versión anterior de C ++, y deseando haber usado el descenso recursivo en su lugar.

Matthias Weiler
fuente
Wow gracias. Me pregunto si realmente tiene sentido pensar en usar algo más poderoso que un CFG para analizar cualquier lenguaje artificial.
Dervin Thunk el
Gran libro para comprender los porqués de C ++. Recomiendo eso y el modelo de objetos Inside the C ++ de Lippman para comprender cómo funciona C ++. Aunque ambos están un poco anticuados, siguen siendo una buena lectura.
Matt Price
"Meta-S" es un motor de análisis sensible al contexto de Quinn Tyler Jackson. No lo he usado, pero él cuenta una historia impresionante. Echa un vistazo a sus comentarios en comp.compilers y visita rnaparse.com/MetaS%20defined.htm
Ira Baxter
@IraBaxter: su x-ref es MIA hoy, y las referencias sólidas al software parecen ser escurridizas (la búsqueda de Google no proporciona buenas pistas, ya sea con 'site: rnaparse.com meta-s' o 'quinn jackson meta- s '; hay partes y piezas, pero meta-s.com conduce a un sitio web no informativo, por ejemplo).
Jonathan Leffler
@ Jonathan: hace un tiempo, acabo de notar su queja. No sé por qué el enlace es malo, pensé que era bueno cuando lo escribí. Quinn solía ser bastante activo en comp.compilers. Parece que Google se está volviendo escamoso, esto es todo lo que puedo encontrar: groups.google.com/group/comp.compilers/browse_thread/thread/… IIRC, firmó los derechos de MetaS para algún equipo en Hawai para volver a comercializar. Dado lo técnicamente extraño que era, en mi humilde opinión, está firmando su sentencia de muerte. Parecía un esquema muy inteligente.
Ira Baxter
12

Sí, C ++ es sensible al contexto, muy sensible al contexto. No puede construir el árbol de sintaxis simplemente analizando el archivo usando un analizador de contexto libre porque en algunos casos necesita conocer el símbolo de conocimiento previo para decidir (es decir, construir una tabla de símbolos mientras analiza).

Primer ejemplo:

A*B;

¿Es esta una expresión de multiplicación?

O

¿Es esta una declaración de Bvariable para ser un puntero de tipo A?

Si A es una variable, entonces es una expresión, si A es tipo, es una declaración de puntero.

Segundo ejemplo

A B(bar);

¿Es este un prototipo de función que toma un argumento de bartipo?

O

¿Esto declara una variable Bde tipo Ay llama al constructor de A con barconstante como inicializador?

Necesita saber nuevamente si bares una variable o un tipo de la tabla de símbolos.

Tercer ejemplo:

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

Este es el caso cuando construir una tabla de símbolos mientras se analiza no ayuda porque la declaración de xey viene después de la definición de la función. Por lo tanto, primero debe explorar la definición de clase y observar las definiciones de método en una segunda pasada, para decir que x * y es una expresión, y no una declaración de puntero o lo que sea.

Calmarius
fuente
1
A B();es una declaración de función incluso en una definición de función. Busque el análisis más irritante ...
Programador
"No se puede construir el árbol de sintaxis simplemente analizando el archivo" FALSE. Mira mi respuesta.
Ira Baxter
10

C ++ se analiza con el analizador GLR. Eso significa que durante el análisis del código fuente, el analizador puede encontrar ambigüedad, pero debe continuar y decidir qué regla gramatical usar más adelante .

mira también

¿Por qué C ++ no se puede analizar con un analizador LR (1)?


Recuerde que la gramática libre de contexto no puede describir TODAS las reglas de una sintaxis de lenguaje de programación. Por ejemplo, la gramática de atributos se usa para verificar la validez de un tipo de expresión.

int x;
x = 9 + 1.0;

No puede describir la siguiente regla con una gramática libre de contexto: El lado derecho de la tarea debe ser del mismo tipo que el lado izquierdo.

AraK
fuente
44
La mayoría de los analizadores C ++ no utilizan la tecnología de análisis GLR. GCC no lo hace. Algunos lo hacen. Consulte semanticdesigns.com/Products/FrontEnds/CppFrontEnd.html para obtener uno que sí lo haga.
Ira Baxter
10

Tengo la sensación de que existe cierta confusión entre la definición formal de "sensible al contexto" y el uso informal de "sensible al contexto". El primero tiene un significado bien definido. Este último se usa para decir "necesita contexto para analizar la entrada".

Esto también se pregunta aquí: sensibilidad al contexto frente a ambigüedad .

Aquí hay una gramática libre de contexto:

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

Es ambiguo, por lo que para analizar la entrada "x" necesita algo de contexto (o vivir con la ambigüedad, o emitir "Advertencia: E8271 - La entrada es ambigua en la línea 115"). Pero ciertamente no es una gramática sensible al contexto.

Omri Barel
fuente
¿Cómo resuelve este problema tener múltiples símbolos en el lado izquierdo de una producción? No creo que esta respuesta esté respondiendo la pregunta.
user541686
1
Mi respuesta es en respuesta a la primera oración: "A menudo escucho afirmaciones de que C ++ es un lenguaje sensible al contexto". Si esas afirmaciones usan informalmente la expresión "sensible al contexto", entonces no hay problema. No creo que C ++ sea formalmente sensible al contexto.
Omri Barel
Creo que C ++ es formalmente sensible al contexto, pero el problema que tengo es que no entiendo cómo una gramática sensible al contexto tendría más éxito al analizar C ++ que un CFG.
user541686
6

Ningún lenguaje similar a Algol está libre de contexto, porque tienen reglas que restringen las expresiones y declaraciones en las que pueden aparecer identificadores en función de su tipo, y porque no hay límite en el número de declaraciones que pueden ocurrir entre la declaración y el uso.

La solución habitual es escribir un analizador sin contexto que realmente acepte un superconjunto de programas válidos y coloque las porciones sensibles al contexto en un código "semántico" ad hoc adjunto a las reglas.

C ++ va mucho más allá de esto, gracias a su sistema de plantillas Turing-complete. Consulte la Pregunta de desbordamiento de pila 794015 .

James Jones
fuente
5

Es sensible al contexto, ya que a b(c);tiene dos parámetros válidos: declaración y variable. Cuando dices "Si ces un tipo", ese es el contexto, allí mismo, y has descrito exactamente cómo C ++ es sensible a él. Si no tuviera ese contexto de "¿Qué es c?" no podrías analizar esto sin ambigüedades.

Aquí, el contexto se expresa en la elección de tokens: el analizador lee un identificador como un token de tipo de nombre si nombra un tipo. Esta es la resolución más simple y evita gran parte de la complejidad de ser sensible al contexto (en este caso).

Editar: Hay, por supuesto, más problemas de sensibilidad al contexto, simplemente me he centrado en el que has mostrado. Las plantillas son especialmente desagradables para esto.

Perrito
fuente
1
Además a<b<c>>d, ¿verdad? (Su ejemplo es en realidad un clásico de C , donde es la única obstrucción para estar libre de contexto.)
Kerrek SB
Creo que es más un problema lexing. Pero ciertamente está en la misma categoría, sí.
Cachorro
2
El interlocutor no pregunta cómo es más sensible al contexto que C, solo para mostrar que es sensible al contexto.
Cachorro
Entonces ... ¿ C ++ es más sensible al contexto que C?
Kerrek SB
2
@DeadMG No creo que estés respondiendo la pregunta (tampoco creo que lo haya hecho). ¿Cómo resuelve este problema tener terminales en el lado izquierdo de una producción?
user541686
5

Las producciones en el estándar C ++ están escritas sin contexto, pero, como todos sabemos, en realidad no definen el lenguaje con precisión. Algo de lo que la mayoría de la gente ve como ambigüedad en el idioma actual podría (creo) resolverse sin ambigüedades con una gramática sensible al contexto.

Para el ejemplo más obvio, consideremos el más irritante de análisis: int f(X);. Si Xes un valor, esto se define fcomo una variable que se inicializará con X. Si Xes un tipo, se define fcomo una función que toma un único parámetro de tipo X.

Mirando eso desde un punto de vista gramatical, podríamos verlo así:

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]

Por supuesto, para ser completamente correctos, necesitaríamos agregar algunas "cosas" adicionales para tener en cuenta la posibilidad de intervenir declaraciones de otros tipos (es decir, A y B deberían ser realmente "declaraciones que incluyan la declaración de X como ..." , o algo en ese orden).

Sin embargo, esto es bastante diferente de un CSG típico (o al menos lo que recuerdo de ellos). Esto depende de la construcción de una tabla de símbolos: la parte que reconoce específicamente Xcomo un tipo o valor, no solo algún tipo de declaración que lo precede, sino el tipo correcto de declaración para el símbolo / identificador correcto.

Como tal, tendría que investigar un poco para estar seguro, pero mi suposición inmediata es que esto realmente no califica como un CSG, al menos como el término se usa normalmente.

Jerry Coffin
fuente
Las producciones (sin contexto) definen el análisis más desconcertante lo suficientemente bien como para que pueda ser analizado por un motor de análisis sin contexto. Eso retrasa el problema de decidir cuál de las múltiples interpretaciones son válidas hasta después de que se complete el análisis, pero eso solo hace que la ingeniería del analizador y la resolución de nombres sea más fácil, porque son modulares en lugar de enredados como en los analizadores C ++ convencionales. Vea AST para la mayoría de los análisis molestos: stackoverflow.com/questions/17388771/…
Ira Baxter
5

El caso más simple de gramática no libre de contexto implica analizar expresiones que involucran plantillas.

a<b<c>()

Esto puede analizar como

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

O

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

Las dos AST solo pueden ser desambiguadas examinando la declaración de 'a': la primera AST si 'a' es una plantilla, o la segunda si no.

Aaron
fuente
Creo que C ++ 11 exige la última interpretación, y tiene que agregar parens para optar por la primera.
Joseph Garvin el
1
@JosephGarvin, no. Mandatos de C ++ que <deben ser un paréntesis si pudieran serlo (por ejemplo, sigue un identificador que nombra una plantilla). C ++ 11 agregó el requisito >y el primer carácter de >>ser interpretado como corchetes si ese uso es plausible. Esto afecta el análisis de a<b>c>dónde ahay una plantilla pero no tiene ningún efecto a<b<c>.
rici
@aaron: ¿cómo es eso más simple que a();(que es expr.callo expr.type.conv)?
rici
@rici: Vaya, no me di cuenta de que era asimétrico.
Joseph Garvin
55
¿Describe ambigüedad o sensibilidad al contexto?
corazza
4

Se ha demostrado que las plantillas de C ++ son Turing Powerful. Aunque no es una referencia formal, aquí hay un lugar para buscar en ese sentido:

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

Me aventuraré a adivinar (tan antiguo como una prueba de CACM folkorica y concisa que muestra que ALGOL en los años 60 no podía ser repetido por un CFG) y decir que C ++ no puede ser analizado correctamente solo por un CFG. CFG, junto con varios mecanismos de TP en un pase de árbol o durante eventos de reducción: esta es otra historia. En un sentido general, debido al problema de detención, existe algún programa de C ++ que no se puede demostrar que sea correcto / incorrecto pero que, sin embargo, es correcto / incorrecto.

{PD- Como autor de Meta-S (mencionado por varias personas arriba), puedo decir con toda seguridad que Thothic no está extinto ni el software está disponible de forma gratuita. Tal vez he redactado esta versión de mi respuesta de tal manera que no me eliminen ni voten por -3.}

Quinn Tyler Jackson
fuente
3

C ++ no está libre de contexto. Lo aprendí hace algún tiempo en la conferencia de compiladores. Una búsqueda rápida le dio este enlace, donde la sección "Sintaxis o semántica" explica por qué C y C ++ no están libres de contexto:

Wikipedia Talk: gramática libre de contexto

Un saludo,
Ovanes

ovanos
fuente
2

Obviamente, si toma la pregunta al pie de la letra, casi todos los idiomas con identificadores son sensibles al contexto.

Es necesario saber si un identificador es un nombre de tipo (un nombre de clase, un nombre introducido por typedef, un parámetro de plantilla de nombre de tipo), un nombre de plantilla o algún otro nombre para poder utilizar correctamente parte del identificador. Por ejemplo:

x = (name)(expression);

es una conversión si namees un nombre de tipo y una llamada a función si namees un nombre de función. Otro caso es el llamado "análisis más irritante" donde no es posible diferenciar la definición de variable y la declaración de función (hay una regla que dice que es una declaración de función).

Esa dificultad ha introducido la necesidad de typenamey templatecon nombres dependientes. El resto de C ++ no es sensible al contexto hasta donde yo sé (es decir, es posible escribir una gramática libre de contexto para él).

Un programador
fuente
2

Meta-S "es un motor de análisis sensible al contexto de Quinn Tyler Jackson. No lo he usado, pero cuenta una historia impresionante. Echa un vistazo a sus comentarios en comp.compilers y visita rnaparse.com/MetaS%20defined.htm - Ira Baxter 25 de julio a las 10:42

El enlace correcto está analizando enigines

Meta-S era propiedad de una empresa extinta llamada Thothic. Puedo enviar una copia gratuita del Meta-S a cualquier persona interesada y la he usado en la investigación de análisis. Tenga en cuenta que la "gramática de pseudoknot" incluida en las carpetas de ejemplos fue escrita por un programador aficionado no bioinformático y básicamente no funciona. Mis gramáticas toman un enfoque diferente y funcionan bastante bien.

algo
fuente
Este es en realidad un hallazgo interesante.
Dervin Thunk
0

Un gran problema aquí es que los términos "libre de contexto" y "sensible al contexto" son poco intuitivos dentro de la informática. Para C ++, la sensibilidad al contexto se parece mucho a la ambigüedad, pero eso no es necesariamente cierto en el caso general.

En C / ++, una instrucción if solo se permite dentro de un cuerpo de función. Eso parecería hacerlo sensible al contexto, ¿verdad? Bueno no. Las gramáticas sin contexto en realidad no necesitan la propiedad donde puede extraer alguna línea de código y determinar si es válida. Eso no es realmente lo que significa sin contexto. Realmente es solo una etiqueta que implica vagamente algo relacionado con lo que parece.

Ahora, si una declaración dentro del cuerpo de una función se analiza de manera diferente dependiendo de algo definido fuera de los antepasados ​​gramaticales inmediatos (por ejemplo, si un identificador describe un tipo o variable), como en el a * b;caso, entonces es, de hecho, sensible al contexto. No hay ambigüedad real aquí; se analizará como una declaración de un puntero si aes un tipo y una multiplicación de lo contrario.

Ser sensible al contexto no significa necesariamente "difícil de analizar". C en realidad no es tan difícil porque la infame a * b;"ambigüedad" se puede resolver con una tabla de símbolos que contiene los typedefmensajes encontrados anteriormente. No requiere ninguna instancia de plantilla arbitraria (que se haya comprobado que es Turing Complete) para resolver ese caso como lo hace C ++ en ocasiones. En realidad, no es posible escribir un programa en C que no se compile en un tiempo finito aunque tenga la misma sensibilidad al contexto que C ++.

Python (y otros lenguajes sensibles al espacio en blanco) también depende del contexto, ya que requiere un estado en el léxico para generar tokens de sangría y sangría, pero eso no hace que sea más difícil de analizar que una gramática LL-1 típica. En realidad, utiliza un generador de analizadores, que es parte de por qué Python tiene mensajes de error de sintaxis tan poco informativos. También es importante tener en cuenta aquí que no hay "ambigüedad" como el a * b;problema en Python, dando un buen ejemplo concreto de un lenguaje sensible al contexto sin gramática "ambigua" (como se menciona en el primer párrafo).

Carne de res
fuente
-4

Esta respuesta dice que C ++ no está libre de contexto ... hay una implicación (no por el respondedor) de que no se puede analizar, y la respuesta ofrece un ejemplo de código difícil que produce un programa C ++ no válido si una constante constante no es un número primo.

Como otros han observado, la pregunta sobre si el lenguaje es sensible al contexto / libre es diferente de la misma pregunta sobre una gramática específica.

Para dejar en reposo la pregunta sobre la capacidad de análisis, ofrezco evidencia empírica de que existen gramáticas libres de contexto para C ++, que se pueden usar para producir un AST para un análisis libre de contexto del texto fuente al analizarlo de hecho con un GLR existente herramienta basada en analizadores que se basa en una gramática explícita.

Sí, tiene éxito al "aceptar demasiado"; no todo lo que acepta es un programa válido de C ++, por lo que se sigue con verificaciones adicionales (verificaciones de tipo). Y sí, el verificador de tipos puede encontrarse con problemas de computabilidad. En la práctica, las herramientas no tienen este problema; si la gente escribiera programas como ese, ninguno de ellos compilaría. (Creo que el estándar realmente pone un límite a la cantidad de cómputo que puede hacer desplegando una plantilla, por lo que, de hecho, el cómputo es en realidad finito pero probablemente bastante grande).

Si lo que quiere decir es determinar si el programa fuente es miembro del conjunto de programas fuente C ++ válidos , entonces estaré de acuerdo en que el problema es mucho más difícil. Pero el problema no es el análisis .

La herramienta resuelve este problema al aislar el análisis de la verificación de tipo del programa analizado. (Cuando hay múltiples interpretaciones en ausencia de contexto, registra un nodo de ambigüedad en el árbol de análisis con varios análisis posibles; la verificación de tipo decide cuál es la correcta y elimina los subárboles no válidos). Puede ver un árbol de análisis (parcial) en el siguiente ejemplo; todo el árbol es demasiado grande para caber en una respuesta SO. Tenga en cuenta que obtiene un árbol de análisis si se usa el valor 234797 o 234799.

Ejecutar el resolutor de tipo / nombre de la herramienta sobre el AST con el valor original 234799 tiene éxito. Con el valor 234797, el solucionador de nombres falla (como se esperaba) con el mensaje de error, "typen no es un tipo". y, por lo tanto, esa versión no es un programa válido de C ++.

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
Ira Baxter
fuente
Determinar si es una declaración de variable o una multiplicación no es una función de verificación de tipo. También tuve que borrar tu respuesta de esas cosas de autopromoción ... otra vez.
Cachorro
@Cachorro: puedes decir lo que quieras, pero así es como funciona la herramienta. Eliminar el nombre de la herramienta probablemente hará que la gente pregunte cuál es el nombre de la herramienta.
Ira Baxter
Si la herramienta funciona así o no es irrelevante, ya que la pregunta no pide el funcionamiento de la herramienta. Además, creo que podemos esperar con seguridad que eso suceda realmente.
Cachorro