O C ++ é livre de contexto ou sensível ao contexto?


405

Costumo ouvir afirmações de que C ++ é uma linguagem sensível ao contexto. Veja o seguinte exemplo:

a b(c);

Essa é uma definição de variável ou uma declaração de função? Isso depende do significado do símbolo c. Se cé uma variável , a b(c);define uma variável com o nome bdo tipo a. É inicializado diretamente com c. Mas se cfor um tipo , a b(c);declara uma função chamada bque recebe cae retorna uma a.

Se você procurar a definição de linguagens livres de contexto, basicamente lhe dirá que todas as regras gramaticais devem ter lados esquerdos que consistem em exatamente um símbolo não terminal. Gramáticas sensíveis ao contexto, por outro lado, permitem seqüências arbitrárias de símbolos terminais e não terminais no lado esquerdo.

Navegando pelo Apêndice A de "A linguagem de programação C ++", não consegui encontrar uma única regra gramatical que contenha mais nada além de um único símbolo não terminal no lado esquerdo. Isso implicaria que C ++ é livre de contexto. (Obviamente, toda linguagem livre de contexto também é sensível ao contexto, no sentido de que as linguagens livres de contexto formam um subconjunto das linguagens sensíveis ao contexto, mas esse não é o ponto.)

Então, o C ++ é livre de contexto ou sensível ao contexto?


12
@CarlNorum Por favor, mostre-me uma única regra gramatical de C ++ que não consiste em um único símbolo não terminal no lado esquerdo e acredito imediatamente em você.
Fredoverflow

9
IIUC depende um pouco de onde você desenha a linha para sensibilidade ao contexto. Acho que vi pessoas argumentando que quase todas as linguagens de programação de tipo estaticamente são sensíveis ao contexto, não porque você não pode criar um compilador prático para elas com as ferramentas de análise de CFG, mas porque essas implementações "trapaceiam" analisando alguns programas inválidos e apenas rejeitando-os mais tarde, durante a verificação de tipo. Portanto, se você considerar que programas mal digitados não estão na linguagem (no sentido de CS, ou seja, um conjunto de strings) que o analisador deve aceitar, mais linguagens que C ++ são sensíveis ao contexto.

6
@DeadMG: Não, você está errado. Não existe "análise" ou "semântica" na teoria formal da linguagem, apenas "linguagem", que é um conjunto de strings.
precisa saber é

27
Até agora, nenhuma resposta abordou sua definição de "gramática livre de contexto". Na minha opinião, a resposta correta para essa pergunta cita uma produção no apêndice A que não se encaixa na sua definição ou demonstra que ela está incorreta ou insuficiente. Fique firme!
Lightness Races in Orbit

8
Veja A gramática de D é realmente livre de contexto? . Na verdade, acho que todo mundo aqui deveria ler essa pergunta e suas respostas!
Lightness Races in Orbit

Respostas:


341

Abaixo está minha demonstração favorita (atual) de por que a análise de C ++ é (provavelmente) concluída em Turing , pois mostra um programa sintaticamente correto se e somente se um número inteiro for primo.

Portanto, afirmo que o C ++ não é isento de contexto nem sensível ao contexto .

Se você permitir seqüências arbitrárias de símbolos nos dois lados de qualquer produção, produza uma gramática Tipo 0 ("irrestrita") na hierarquia de Chomsky , que é mais poderosa que uma gramática sensível ao contexto; gramáticas irrestritas são completas de Turing. Uma gramática sensível ao contexto (Tipo 1) permite vários símbolos de contexto no lado esquerdo de uma produção, mas o mesmo contexto deve aparecer no lado direito da produção (daí o nome "sensível ao contexto"). [1] Gramáticas sensíveis ao contexto são equivalentes a máquinas de Turing de limite linear .

No programa de exemplo, o cálculo principal pode ser realizado por uma máquina de Turing de limite linear, portanto não prova a equivalência de Turing, mas a parte importante é que o analisador precisa executar o cálculo para realizar a análise sintática. Poderia ter sido qualquer computação expressável como uma instanciação de modelo e há todos os motivos para acreditar que a instanciação de modelo C ++ é Turing-complete. Veja, por exemplo, o artigo de Todd L. Veldhuizen em 2003 .

Independentemente disso, o C ++ pode ser analisado por um computador, portanto certamente pode ser analisado por uma máquina de Turing. Conseqüentemente, uma gramática irrestrita poderia reconhecê-la. Escrever essa gramática seria impraticável, e é por isso que o padrão não tenta fazê-lo. (Ver abaixo.)

O problema com a "ambiguidade" de certas expressões é principalmente um arenque vermelho. Para começar, a ambiguidade é um recurso de uma gramática específica, não de um idioma. Mesmo que se prove que um idioma não possui gramáticas inequívocas, se pode ser reconhecido por uma gramática livre de contexto, é livre de contexto. Da mesma forma, se não puder ser reconhecido por uma gramática livre de contexto, mas puder ser reconhecido por uma gramática sensível ao contexto, é sensível ao contexto. A ambiguidade não é relevante.

Mas, de qualquer forma, como a linha 21 (ou seja auto b = foo<IsPrime<234799>>::typen<1>();) no programa abaixo, as expressões não são ambíguas; eles são simplesmente analisados ​​de maneira diferente, dependendo do contexto. Na expressão mais simples do problema, a categoria sintática de certos identificadores depende de como eles foram declarados (tipos e funções, por exemplo), o que significa que a linguagem formal precisaria reconhecer o fato de que duas seqüências de comprimento arbitrário em o mesmo programa é idêntico (declaração e uso). Isso pode ser modelado pela gramática "copy", que é a gramática que reconhece duas cópias exatas consecutivas da mesma palavra. É fácil provar com o lema de bombeamentoque essa linguagem não é livre de contexto. Uma gramática sensível ao contexto para esse idioma é possível e uma gramática Tipo 0 é fornecida na resposta a esta pergunta: /math/163830/context-sensitive-grammar-for-the- linguagem de cópia .

Se alguém tentasse escrever uma gramática sensível ao contexto (ou irrestrita) para analisar o C ++, isso provavelmente preencheria o universo com rabiscos. Escrever uma máquina de Turing para analisar C ++ seria uma tarefa igualmente impossível. Até escrever um programa em C ++ é difícil, e até onde eu sei, nenhum deles foi provado correto. É por isso que o padrão não tenta fornecer uma gramática formal completa e por que escolhe escrever algumas das regras de análise em inglês técnico.

O que parece uma gramática formal no padrão C ++ não é a definição formal completa da sintaxe da linguagem C ++. Nem sequer é a definição formal completa da linguagem após o pré-processamento, que pode ser mais fácil de formalizar. (Porém, essa não seria a linguagem: a linguagem C ++, conforme definida pelo padrão, inclui o pré-processador, e a operação do pré-processador é descrita algoritmicamente, pois seria extremamente difícil de descrever em qualquer formalismo gramatical. É nessa seção. do padrão em que a decomposição lexical é descrita, incluindo as regras em que deve ser aplicada mais de uma vez.)

As várias gramáticas (duas gramáticas sobrepostas para análise lexical, uma que ocorre antes do pré-processamento e a outra, se necessário, posteriormente, além da gramática "sintática") são coletadas no Apêndice A, com esta nota importante (ênfase adicionada):

Este resumo da sintaxe do C ++ pretende ser um auxílio à compreensão. Não é uma afirmação exata do idioma . Em particular, a gramática descrita aqui aceita um superconjunto de construções válidas em C ++ . Regras de desambiguação (6.8, 7.1, 10.2) devem ser aplicadas para distinguir expressões de declarações. Além disso, regras de controle de acesso, ambiguidade e tipo devem ser usadas para eliminar construções sintaticamente válidas, mas sem sentido.

Finalmente, aqui está o programa prometido. A linha 21 está sintaticamente correta se e somente se o N in IsPrime<N>for primo. Caso contrário, typené um número inteiro, não um modelo, portanto, typen<1>()é analisado como (typen<1)>()sintaticamente incorreto, porque ()não é uma expressão sintaticamente 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 colocá-lo mais tecnicamente, toda produção em uma gramática sensível ao contexto deve ter a forma:

αAβ → αγβ

onde Aé um terminal e não α, βsão possivelmente sequências vazias de símbolos gramaticais e γé uma sequência não vazia. (Os símbolos gramaticais podem ser terminais ou não terminais).

Isso pode ser lido como A → γapenas no contexto [α, β]. Em uma gramática livre de contexto (Tipo 2) αe βdeve estar vazia.

Acontece que você também pode restringir gramáticas com a restrição "monotônica", em que toda produção deve ter a forma:

α → βonde |α| ≥ |β| > 0  ( |α|significa "o comprimento de α")

É possível provar que o conjunto de idiomas reconhecidos pelas gramáticas monotônicas é exatamente o mesmo que o conjunto de idiomas reconhecidos pelas gramáticas sensíveis ao contexto, e geralmente é mais fácil basear as provas em gramáticas monotônicas. Consequentemente, é bastante comum ver "sensível ao contexto" usado como se quisesse "monotônico".


27
Portanto, não é apenas sensível ao contexto, mas também pode depender de qualquer contexto que você possa expressar em modelos, que são completos para Turing.
Filhote de cachorro

7
@mehrdad, o OP diz "linguagem sensível ao contexto", não gramática sensível ao contexto. A ambiguidade é uma característica de uma gramática, não de um idioma. A linguagem é realmente sensível ao contexto, mas não porque uma gramática específica seja ambígua.
rici

2
Observe que meu exemplo não é ambíguo. É uma expressão inequívoca de um programa válido. Se você alterar o valor na linha 21, ele poderá ficar mal formado. Mas em nenhum dos casos é ambíguo.
rici

5
Tenho uma dúvida: como você mostra, o resultado da avaliação do modelo pode fazer a diferença entre um programa bem formado e um mal formado. A avaliação do modelo está completa. Portanto, determinar corretamente se uma sequência de caracteres está na linguagem (C ++) exigiria a conclusão completa? Como você diz, uma linguagem sensível ao contexto é "apenas" um "autômato limitado linear", que não é um AFAIK completo. Ou seu argumento está usando os limites que o padrão C ++ impõe em algumas coisas, incluindo a profundidade da avaliação do modelo?

4
@AntonGolov: Minha versão original desse exemplo fez exatamente isso (você pode obtê-lo colocando 0dentro de (), por um simples), mas acho que é mais interessante dessa maneira, porque demonstra que você precisa da instanciação do modelo para reconhecer se uma string é um programa C ++ sintaticamente correto. Se os dois ramos forem compilados, eu teria que trabalhar mais para contestar o argumento de que a diferença é "semântica". Curiosamente, embora eu estou muitas vezes desafiados a definir "sintática", ninguém jamais ofereceu uma definição de "semântica" diferente de "coisas que eu não acho que é sintática" :)
rici

115

Primeiro, você observou corretamente que não existem regras sensíveis ao contexto na gramática no final do padrão C ++, de modo que a gramática é livre de contexto.

No entanto, essa gramática não descreve com precisão a linguagem C ++, porque produz programas não C ++, como

int m() { m++; }

ou

typedef static int int;

A linguagem C ++ definida como "o conjunto de programas C ++ bem formados" não é livre de contexto (é possível mostrar que apenas exigir variáveis ​​a serem declaradas o torna). Dado que você pode escrever teoricamente programas completos de Turing em modelos e criar um programa mal formado com base em seus resultados, isso nem é sensível ao contexto.

Agora, pessoas (ignorantes) (geralmente não teóricas da linguagem, mas designers de analisadores) normalmente usam "sem contexto" em alguns dos seguintes significados

  • ambíguo
  • não pode ser analisado com Bison
  • não LL (k), LR (k), LALR (k) ou qualquer classe de idioma definida pelo analisador que eles escolheram

A gramática na parte de trás do padrão não satisfaz essas categorias (ou seja, é ambígua, não LL (k) ...), portanto, a gramática C ++ "não é livre de contexto" para elas. E, de certa forma, eles estão certos de que é muito difícil produzir um analisador de C ++ funcional.

Observe que as propriedades aqui usadas são apenas fracamente conectadas a linguagens livres de contexto - a ambiguidade não tem nada a ver com sensibilidade ao contexto (na verdade, regras sensíveis ao contexto geralmente ajudam a desambiguar produções), as outras duas são meramente subconjuntos de contexto idiomas livres. E a análise de linguagens sem contexto não é um processo linear (embora a análise de linguagens determinísticas seja).


7
ambiguity doesn't have anything to do with context-sensitivityEssa também foi minha intuição, por isso estou feliz em ver alguém (a) concordar e (b) explicá-la onde não podia. Eu acredito que desqualifica todos os argumentos que se baseiam a b(c);e satisfaz parcialmente a pergunta original cuja premissa foi "freqüentemente ouvida" reivindicações de sensibilidade ao contexto devido à ambiguidade ... especialmente quando, para a gramática , na verdade não há ambiguidade, mesmo na MVP.
Lightness Races in Orbit

6
@KonradRudolph: O que o padrão diz é "Existe uma quantidade definida pela implementação que especifica o limite da profundidade total das instanciações recursivas, que pode envolver mais de um modelo. O resultado de uma recursão infinita na instanciação é indefinido". (14.7.1p15) Interpreto que isso significa que uma implementação não é necessária para entender todos os programas válidos em c ++, e que programas com uma profundidade de recursão muito grande são inválidos. Os únicos marcados como inválidos são aqueles com uma profundidade de recursão infinita.
rici 29/01

3
@ KonradRudolph: Eu discuto que é "referência geral". O fato de eu ler esse artigo bastante complexo e não o compreender suficientemente para descobrir esse pequeno fato deve ser suficiente para demonstrar isso. Não é como se você dissesse algo como "computadores geralmente usam eletricidade" ou "bits podem ser verdadeiros ou falsos".
Lightness Races in Orbit

3
Se esse fato é tão amplamente conhecido, eu acho que seria muito mais fácil encontrar uma referência a ele do que discutir longamente se um deles deve ou não ser fornecido. Sem mencionar construtivo.
Samuel Edwin Ward

5
Até onde eu sei, @Konrad se enganou quando disse: "Sensível ao contexto é equivalente a Turing completo". (pelo menos, ele estava se estivesse denotando "recursivamente enumerável" por "Turing complete") e, desde então, não conseguiu reconhecer esse erro. Aqui é uma referência para o relacionamento inclusão conjunto apropriado envolvido aqui: en.wikipedia.org/wiki/Chomsky_hierarchy
pnkfelix

61

Sim. A expressão a seguir possui uma ordem diferente de operações, dependendo do tipo de contexto resolvido :

Editar: quando a ordem real da operação varia, torna-se incrivelmente difícil usar um compilador "regular" que analise um AST não decorado antes de decorá-lo (propagando informações de tipo). Outras coisas sensíveis ao contexto mencionadas são "bastante fáceis" em comparação com isso (não que a avaliação do modelo seja 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 );

Por que esse problema não pode ser resolvido como em C, lembrando quais definições de tipo estão no escopo?
Blaisorblade

11
@ Blaisorblade: Uma maneira de tornar um compilador "limpo" é separar tarefas em etapas independentes em uma cadeia, como criar uma árvore de análise a partir da entrada, seguida por uma etapa que faz a análise de tipo. O C ++ obriga você a 1) mesclar essas etapas em uma ou 2) analisar o documento de acordo com as duas / todas as interpretações possíveis, e permitindo que os estágios de resolução de tipo reduzam a interpretação correta.
Sam Harwell

@ 280Z28: concordou, mas esse também é o caso de C; Eu acho que uma boa resposta a esta pergunta deve mostrar porque C ++ é pior do que C. A tese de doutoramento ligados aqui faz isso: stackoverflow.com/a/243447/53974
Blaisorblade

26

Para responder sua pergunta, você precisa distinguir duas perguntas diferentes.

  1. A mera sintaxe de quase todas as linguagens de programação é livre de contexto. Normalmente, é fornecido como uma forma estendida de Backus-Naur ou gramar sem contexto.

  2. No entanto, mesmo que um programa esteja em conformidade com o gramar sem contexto definido pela linguagem de programação, ele não é necessariamente um programa válido . Existem muitas propriedades sem contexto que um programa precisa satisfazer para ser um programa válido. Por exemplo, a propriedade mais simples é o escopo das variáveis.

Para concluir, se o C ++ é ou não livre de contexto depende da pergunta que você faz.


5
É interessante notar que você geralmente precisa colocar o nível de "mera sintaxe" abaixo do esperado, para obter um CFG para sua linguagem de programação. Veja C, por exemplo. Você pode pensar que a regra gramatical para uma declaração simples de variável em C seria VARDECL : TYPENAME IDENTIFIER, mas não pode tê-la, porque não é possível distinguir nomes de tipos de outros identificadores no nível CF. Outro exemplo: no nível CF, você não pode decidir se deve analisar a*bcomo uma declaração variável ( bdo tipo ponteiro para a) ou como uma multiplicação.
LaC

2
@LaC: Sim, obrigado por apontar isso! A propósito, tenho certeza de que existe um termo técnico mais comum para mera sintaxe . Alguém tem o termo correto?
Dan

4
@ Dan: o que você está falando é uma aproximação da linguagem dada por alguma gramática livre de contexto. É claro que essa aproximação é livre de co-texto por definição. Este é o sentido em que a "sintaxe" é frequentemente usada ao discutir linguagens de programação.
Reinierpost

13

Você pode dar uma olhada no The Design & Evolution of C ++ , de Bjarne Stroustrup. Nele, ele descreve seus problemas ao tentar usar o yacc (ou similar) para analisar uma versão anterior do C ++, e desejando ter usado descida recursiva.


Uau ... obrigado. Gostaria de saber se realmente faz sentido pensar em usar algo mais poderoso que um CFG para analisar qualquer linguagem artificial.
Dervin Thunk

Ótimo livro para entender os porquês do C ++. Eu recomendo isso e o Inside the C ++ Object Model de Lippman para entender como o C ++ funciona. Embora ambos sejam um pouco datados, ainda são uma boa leitura.
Matt Price

"Meta-S" é um mecanismo de análise sensível ao contexto de Quinn Tyler Jackson. Eu não usei, mas ele conta uma história impressionante. Confira seus comentários em comp.compilers e ver rnaparse.com/MetaS%20defined.htm
Ira Baxter

@IraBaxter: seu x-ref é MIA hoje - e referências sólidas ao software parecem ilusórias (a pesquisa no Google não fornece bons leads, seja com 'site: rnaparse.com meta-s' ou 'quinn jackson meta- s '; existem partes, mas o meta-s.com leva a um site não informativo, por exemplo).
23611 Jonathan Leffler

@ Jonathan: faz um tempo, só notei sua reclamação. Não sei por que o link está ruim, eu pensei que era bom quando o escrevi. Quinn costumava ser bastante ativo nos comp.compilers. O Google parece estar esquisito, é tudo o que consigo encontrar: groups.google.com/group/comp.compilers/browse_thread/thread/… IIRC, ele assinou os direitos do MetaS para algumas roupas no Havaí para revender. Dado o quão tecnicamente estranho isso foi, IMHO está assinando sua sentença de morte. Parecia um esquema muito inteligente.
Ira Baxter

12

Sim, o C ++ é sensível ao contexto, muito sensível ao contexto. Você não pode construir a árvore de sintaxe simplesmente analisando o arquivo usando um analisador sem contexto, porque em alguns casos você precisa conhecer o símbolo do conhecimento anterior para decidir (por exemplo, criar uma tabela de símbolos durante a análise).

Primeiro exemplo:

A*B;

Esta é uma expressão de multiplicação?

OU

Esta declaração de Bvariável é um ponteiro do tipo A?

Se A é uma variável, então é uma expressão, se A é do tipo, é uma declaração de ponteiro.

Segundo exemplo:

A B(bar);

Este é um protótipo de função usando um argumento do bartipo?

OU

Essa variável Bde declaração é do tipo Ae chama o construtor de A com barconstante como inicializador?

Você precisa saber novamente se baré uma variável ou um tipo da tabela de símbolos.

Terceiro exemplo:

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

Este é o caso ao criar a tabela de símbolos durante a análise não ajuda, porque a declaração de xey vem após a definição da função. Portanto, você precisa examinar primeiro a definição da classe e examinar as definições do método em uma segunda passagem, para dizer que x * y é uma expressão e não uma declaração de ponteiro ou o que seja.


11
A B();é uma declaração de função, mesmo em uma definição de função. Olhar para a maioria de análise irritante ...
AProgrammer

"Você não pode construir a árvore de sintaxe simplesmente analisando o arquivo" FALSE. Veja minha resposta.
Ira Baxter

10

C ++ é analisado com analisador GLR. Isso significa que, durante a análise do código-fonte, o analisador pode encontrar ambiguidade, mas deve continuar e decidir qual regra gramatical usar mais tarde .

veja também

Por que o C ++ não pode ser analisado com um analisador LR (1)?


Lembre-se de que a gramática livre de contexto não pode descrever TODAS as regras de uma sintaxe da linguagem de programação. Por exemplo, a gramática de atributo é usada para verificar a validade de um tipo de expressão.

int x;
x = 9 + 1.0;

Você não pode descrever a regra a seguir com gramática livre de contexto: O lado direito da atribuição deve ser do mesmo tipo do lado esquerdo.


4
A maioria dos analisadores de C ++ não usa a tecnologia de análise GLR. O GCC não. Alguns fazem. Consulte semanticdesigns.com/Products/FrontEnds/CppFrontEnd.html para obter um exemplo disso.
Ira Baxter

10

Sinto que há alguma confusão entre a definição formal de "sensível ao contexto" e o uso informal de "sensível ao contexto". O primeiro tem um significado bem definido. O último é usado para dizer "você precisa do contexto para analisar a entrada".

Isso também é perguntado aqui: Sensibilidade ao contexto vs Ambiguidade .

Aqui está uma gramática livre de contexto:

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

É ambíguo, portanto, para analisar a entrada "x", você precisa de algum contexto (ou vive com a ambiguidade ou emite "Aviso: E8271 - A entrada é ambígua na linha 115"). Mas certamente não é uma gramática sensível ao contexto.


Como ter vários símbolos no lado esquerdo de uma produção resolve esse problema? Não acho que esta resposta esteja respondendo à pergunta.
user541686

11
Minha resposta é em resposta à primeira frase: "Costumo ouvir alegações de que C ++ é uma linguagem sensível ao contexto". Se essas afirmações usarem a expressão "sensível ao contexto" informalmente, não haverá problema. Eu não acho que o C ++ seja formalmente sensível ao contexto.
Omri Barel 29/01

Acho que o C ++ é formalmente sensível ao contexto, mas o problema que tenho é que não entendo como uma gramática sensível ao contexto teria mais sucesso na análise de C ++ do que um CFG.
user541686

6

Nenhuma linguagem semelhante ao Algol é livre de contexto, porque elas possuem regras que restringem expressões e instruções nas quais os identificadores podem aparecer com base em seu tipo e porque não há limite no número de instruções que podem ocorrer entre a declaração e o uso.

A solução usual é escrever um analisador sem contexto que realmente aceite um superconjunto de programas válidos e coloque as partes sensíveis ao contexto no código "semântico" ad hoc anexado às regras.

O C ++ vai muito além disso, graças ao seu sistema de modelos Turing-complete. Consulte Pergunta sobre estouro de pilha 794015 .




5

É sensível ao contexto, pois a b(c);possui duas declarações e análises válidas. Quando você diz "Se cé um tipo", esse é o contexto, e você descreveu exatamente como o C ++ é sensível a ele. Se você não tivesse esse contexto de "O que é c?" você não poderia analisar isso sem ambiguidade.

Aqui, o contexto é expresso na escolha dos tokens - o analisador lê um identificador como um token de nome de tipo, se nomear um tipo. Essa é a resolução mais simples e evita grande parte da complexidade de ser sensível ao contexto (neste caso).

Editar: é claro que há mais questões de sensibilidade ao contexto, concentrei-me apenas na que você mostrou. Modelos são especialmente desagradáveis ​​para isso.


11
Além disso a<b<c>>d, certo? (O seu exemplo é realmente um clássico do C , onde é a única obstrução de ser livre de contexto.)
Kerrek SB

Isso é mais uma questão irritante, eu acho. Mas certamente está na mesma categoria, sim.
Filhote de cachorro

2
O interlocutor não pergunta como é mais sensível ao contexto que C, apenas para mostrar que é sensível ao contexto.
Filhote de cachorro

Então .. é C ++ mais sensível ao contexto do que C?
Kerrek SB

2
@DeadMG Acho que você não está respondendo à pergunta (também não acho). Como ter terminais no lado esquerdo de uma produção resolve esse problema?
user541686

5

As produções no padrão C ++ são escritas sem contexto, mas como todos sabemos, na verdade, não definimos a linguagem com precisão. Parte do que a maioria das pessoas vê como ambiguidade no idioma atual pode (acredito) ser resolvida sem ambiguidade com uma gramática sensível ao contexto.

Para o exemplo mais óbvio, vamos considerar o mais irritante Parse: int f(X);. E seX é um valor, isso define fcomo uma variável que será inicializada com X. Se Xé um tipo, define fcomo uma função que aceita um único parâmetro do tipo X.

Observando isso do ponto de vista gramatical, poderíamos vê-lo assim:

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]

É claro que, para estar totalmente correto, precisaríamos adicionar algumas "coisas" extras para explicar a possibilidade de declarações de outros tipos intervenientes (ou seja, A e B deveriam realmente ser "declarações incluindo a declaração de X como ..." ou algo nessa ordem).

Isso ainda é bastante diferente de um CSG típico (ou pelo menos o que eu me lembro deles). Isso depende da construção de uma tabela de símbolos - a parte que reconhece especificamente Xcomo um tipo ou valor, não apenas algum tipo de instrução anterior a isso, mas o tipo correto de instrução para o símbolo / identificador correto.

Como tal, eu teria que fazer algumas coisas para ter certeza, mas meu palpite imediato é que isso realmente não se qualifica como CSG, pelo menos como o termo é normalmente usado.


As produções (sem contexto) definem a análise mais irritante o suficiente para que ela possa ser analisada por um mecanismo de análise sem contexto. Isso atrasa o problema de decidir quais interpretações múltiplas são válidas até a conclusão da análise, mas apenas facilita a engenharia do analisador e do resolvedor de nomes, porque elas são modulares e não emaranhadas, como nos analisadores C ++ convencionais. Veja o AST para a maioria das análises irritantes: stackoverflow.com/questions/17388771/…
Ira Baxter,

5

O caso mais simples da gramática sem contexto envolve a análise de expressões envolvendo modelos.

a<b<c>()

Isso pode analisar como

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

Ou

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

Os dois ASTs só podem ser desambiguados examinando a declaração de 'a' - o primeiro AST se 'a' for um modelo, ou o último se não.


Eu acredito que o C ++ 11 exige a última interpretação, e você precisa adicionar parênteses para optar pela primeira.
Joseph Garvin

11
@JosephGarvin, não. O C ++ determina que <deve ser um colchete, se puder (por exemplo, segue um identificador que nomeia um modelo). O C ++ 11 adicionou o requisito de que >o primeiro caractere >>seja interpretado como colchetes próximos se esse uso for plausível. Isso afeta a análise de a<b>c>onde aestá um modelo, mas não tem efeito a<b<c>.
rici 31/01

@ Aaron: como isso é mais simples do que a();(que é um expr.callou expr.type.conv)?
rici 31/01

@rici: Opa, eu não sabia que era assimétrico.
Joseph Garvin

5
Você está descrevendo ambiguidade ou sensibilidade ao contexto?
Corazza

4

Os modelos C ++ foram mostrados como Turing Powerful. Embora não seja uma referência formal, aqui está um lugar para procurar a esse respeito:

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

Vou arriscar um palpite (tão antigo quanto uma prova folclórica e concisa do CACM mostrando que o ALGOL nos anos 60 não poderia ser reprimido por um CFG) e dizer que o C ++ não pode, portanto, ser corretamente analisado apenas por um CFG. CFGs, em conjunto com vários mecanismos de TP em uma passagem em árvore ou durante eventos de redução - essa é outra história. De um modo geral, devido ao Problema de Interrupção, existe algum programa C ++ que não pode ser mostrado como correto / incorreto, mas que é correto / incorreto.

{PS- Como autor do Meta-S (mencionado por várias pessoas acima) - posso dizer com toda certeza que o Thothic não está extinto, nem o software está disponível gratuitamente. Talvez eu tenha redigido esta versão da minha resposta de forma que não seja excluído ou votado para -3.}


3

C ++ não é livre de contexto. Eu aprendi isso há algum tempo na palestra dos compiladores. Uma pesquisa rápida deu esse link, onde a seção "Sintaxe ou semântica" explica por que C e C ++ não são livres de contexto:

Wikipedia Discussão: Gramática livre de contexto

Atenciosamente,
Ovanes


2

Obviamente, se você fizer a pergunta literalmente, quase todos os idiomas com identificadores são sensíveis ao contexto.

É necessário saber se um identificador é um nome de tipo (um nome de classe, um nome introduzido por typedef, um parâmetro de modelo de nome de tipo), um nome de modelo ou outro nome para poder corrigir corretamente parte do uso do identificador. Por exemplo:

x = (name)(expression);

é um cast se namefor um nome de tipo e uma chamada de função sename for um nome de função. Outro caso é a chamada "análise mais irritante", em que não é possível diferenciar a definição de variável e a declaração de função (existe uma regra dizendo que é uma declaração de função).

Essa dificuldade introduziu a necessidade typenamee templatecom nomes dependentes. O resto do C ++ não é sensível ao contexto, tanto quanto eu sei (ou seja, é possível escrever uma gramática livre de contexto para ele).


2

Meta-S "é um mecanismo de análise sensível ao contexto de Quinn Tyler Jackson. Eu não o usei, mas ele conta uma história impressionante. Confira seus comentários em comp.compilers e consulte rnaparse.com/MetaS%20defined.htm - Ira Baxter 25/07 às 10:42

O link correto está analisando os mecanismos

Meta-S era propriedade de uma empresa extinta chamada Thothic. Posso enviar uma cópia gratuita do Meta-S para qualquer pessoa interessada e usei-a na pesquisa de análise de rna. Observe que a "gramática do pseudo-nó" incluída nas pastas de exemplos foi escrita por um programador não bioinformático, amador e basicamente não funciona. Minhas gramáticas adotam uma abordagem diferente e funcionam muito bem.


Este é realmente um achado interessante.
Dervin Thunk

0

Um grande problema aqui é que os termos "livre de contexto" e "sensível ao contexto" são um pouco pouco intuitivos na ciência da computação. Para C ++, a sensibilidade ao contexto parece muito com ambiguidade, mas isso não é necessariamente verdade no caso geral.

Em C / ++, uma instrução if é permitida apenas dentro de um corpo de função. Isso parece torná-lo sensível ao contexto, certo? Bem não. Gramáticas sem contexto não precisam da propriedade onde você pode obter alguma linha de código e determinar se é válida. Na verdade, não é isso que significa livre de contexto. É realmente apenas um rótulo que implica vagamente algo meio relacionado ao que parece.

Agora, se uma declaração dentro de um corpo de função for analisada de maneira diferente, dependendo de algo definido fora dos ancestrais gramaticais imediatos (por exemplo, se um identificador descreve um tipo ou variável), como no exemplo a * b; caso, é, de fato, sensível ao contexto. Não há ambiguidade real aqui; será analisado como uma declaração de um ponteiro se afor de um tipo e multiplicação.

Ser sensível ao contexto não significa necessariamente "difícil de analisar". C na verdade não é tão difícil porque a infame a * b;"ambiguidade" pode ser resolvida com uma tabela de símbolos contendo typedefs encontrados anteriormente. Ele não requer instâncias arbitrárias de modelos (que foram comprovadas como Turing Complete) para resolver esse caso, como o C ++ ocasionalmente. Na verdade, não é possível escrever um programa em C que não seja compilado em um período finito de tempo, mesmo que tenha a mesma sensibilidade ao contexto que o C ++.

O Python (e outras linguagens sensíveis a espaço em branco) também depende do contexto, pois exige que o estado no lexer gere tokens de indentação e dedução, mas isso não dificulta a análise do que uma gramática LL-1 típica. Na verdade, ele usa um gerador de analisador, que é parte do motivo pelo qual o Python possui mensagens de erro de sintaxe não informativas. Também é importante observar aqui que não há "ambiguidade" como o a * b;problema no Python, fornecendo um bom exemplo concreto de uma linguagem sensível ao contexto sem gramática "ambígua" (como mencionado no primeiro parágrafo).


-4

Esta resposta diz que C ++ não é livre de contexto ... há uma implicação (não pelo respondente) de que ele não pode ser analisado, e a resposta oferece um exemplo de código difícil que produz um programa C ++ inválido se uma determinada constante não for uma número primo.

Como outros observaram, a pergunta sobre se a linguagem é sensível ao contexto / livre é diferente da mesma pergunta sobre uma gramática específica.

Para deixar a questão da parseabilidade em repouso, ofereço evidências empíricas de que existem gramáticas sem contexto para C ++, que podem ser usadas para produzir um AST para uma análise sem contexto do texto de origem, na verdade, analisando-o com uma GLR existente ferramenta baseada em analisador, dirigida por uma gramática explícita.

Sim, consegue "aceitar demais"; nem tudo o que ele aceita é um programa C ++ válido, e é por isso que é seguido de verificações adicionais (verificações de tipo). E sim, o verificador de tipos pode ter problemas de computabilidade. Na prática, as ferramentas não têm esse problema; se as pessoas escrevessem programas como esse, nenhum deles seria compilado. (Eu acho que o padrão na verdade limita a quantidade de computação que você pode desdobrar um modelo, então, na verdade, a computação é finita, mas provavelmente muito grande).

Se o que você quer dizer é determinar se o programa de origem é membro do conjunto de programas de origem válidos em C ++ , concordarei que o problema é muito mais difícil. Mas não é a análise que é o problema.

A ferramenta resolve esse problema isolando a análise da verificação de tipo do programa analisado. (Quando houver várias interpretações na ausência de contexto, ele registra uma ambiguidade nó de na árvore de análise com várias análises possíveis; a verificação de tipo decide qual está correta e elimina as subárvores inválidas). Você pode ver uma árvore de análise (parcial) no exemplo abaixo; a árvore inteira é muito grande para caber em uma resposta SO. Observe que você obtém uma árvore de análise se o valor 234797 ou 234799 é usado.

A execução do resolvedor de nome / tipo da ferramenta no AST com o valor original 234799 é bem-sucedida. Com o valor 234797, o resolvedor de nomes falha (conforme o esperado) com a mensagem de erro "typen não é um tipo". e, portanto, essa versão não é um programa C ++ válido.

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

Determinar se é uma declaração de variável ou multiplicação não é um recurso de verificação de tipo. Também tive que esfregar sua resposta sobre essas coisas de autopromoção ... de novo.
Filhote de cachorro

@ Filhote de cachorro: você pode dizer o que quiser, mas é assim que a ferramenta funciona. A exclusão do nome da ferramenta provavelmente fará as pessoas perguntarem qual é o nome da ferramenta.
Ira Baxter

Se é assim que a ferramenta funciona ou não é irrelevante, uma vez que a pergunta não pede o funcionamento da ferramenta. Além disso, acho que podemos esperar com segurança que isso realmente aconteça.
Filhote
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.