c++是上下文无关的还是上下文敏感的?

时间:2021-07-14 00:50:01

I often hear claims that C++ is a context-sensitive language. Take the following example:

我经常听说c++是一种上下文敏感的语言。下面的例子:

a b(c);

Is this a variable definition or a function declaration? That depends on the meaning of the symbol c. If c is a variable, then a b(c); defines a variable named b of type a. It is directly initialized with c. But if c is a type, then a b(c); declares a function named b that takes a c and returns an a.

这是变量定义还是函数声明?这取决于符号c的含义。如果c是一个变量,那么b(c);定义a类型的变量b,直接用c初始化,但如果c是a类型,则是b(c);声明一个名为b的函数,它使用c并返回a。

If you look up the definition of context-free languages, it will basically tell you that all grammar rules must have left-hand sides that consist of exactly one non-terminal symbol. Context-sensitive grammars, on the other hand, allow arbitrary strings of terminal and non-terminal symbols on the left-hand side.

如果你查找上下文无关语言的定义,它会告诉你所有的语法规则都必须包含一个非终结符号。上下文敏感的语法,另一方面,允许任意字符串的终端和非终端符号在左手边。

Browsing through Appendix A of "The C++ Programming Language", I couldn't find a single grammar rule that had anything else besides a single non-terminal symbol on its left-hand side. That would imply that C++ is context-free. (Of course, every context-free language is also context-sensitive in the sense that the context-free languages form a subset of the context-sensitive languages, but that is not the point.)

浏览“c++编程语言”的附录A,我找不到一个语法规则,它除了左手边的一个非终结符之外,还有其他的东西。这意味着c++没有上下文。(当然,每一种上下文无关的语言都是上下文相关的,因为上下文无关的语言构成了上下文相关语言的一个子集,但这不是重点。)

So, is C++ context-free or context-sensitive?

那么,c++上下文无关还是上下文敏感呢?

19 个解决方案

#1


308  

Below is my (current) favorite demonstration of why parsing C++ is (probably) Turing-complete, since it shows a program which is syntactically correct if and only if a given integer is prime.

下面是我(当前)最喜欢的关于解析c++为什么(可能)是Turing-complete的演示,因为它显示了一个在给定整数为素数时语法正确的程序。

So I assert that C++ is neither context-free nor context-sensitive.

因此,我断言c++既不是上下文无关的,也不是上下文敏感的。

If you allow arbitrary symbol sequences on both sides of any production, you produce an Type-0 grammar ("unrestricted") in the Chomsky hierarchy, which is more powerful than a context-sensitive grammar; unrestricted grammars are Turing-complete. A context-sensitive (Type-1) grammar allows multiple symbols of context on the left hand side of a production, but the same context must appear on the right hand side of the production (hence the name "context-sensitive"). [1] Context-sensitive grammars are equivalent to linear-bounded Turing machines.

如果您允许任意符号序列出现在任何产品的两端,那么您将在乔姆斯基层次结构中生成类型-0语法(“无限制”),它比上下文敏感的语法更强大;无限制的语法是图灵完备的。上下文敏感(Type-1)语法允许在生产的左手端有多个上下文符号,但是相同的上下文必须出现在生产的右手端(因此命名为“上下文敏感”)。[1]上下文敏感文法相当于线性有界图灵机。

In the example program, the prime computation could be performed by a linear-bounded Turing machine, so it does not quite prove Turing equivalence, but the important part is that the parser needs to perform the computation in order to perform syntactic analysis. It could have been any computation expressible as a template instantiation and there is every reason to believe that C++ template instantiation is Turing-complete. See, for example, Todd L. Veldhuizen's 2003 paper.

在示例程序中,主计算可以由线性有界的图灵机来执行,因此它并不完全证明图灵等价,但重要的是解析器需要执行计算,以执行句法分析。它可以是任何可表示为模板实例化的计算,并且有充分的理由相信c++模板实例化是Turing-complete的。比如,托德·l·维尔德休岑(Todd L. Veldhuizen) 2003年的论文。

Regardless, C++ can be parsed by a computer, so it could certainly be parsed by a Turing machine. Consequently, an unrestricted grammar could recognize it. Actually writing such a grammar would be impractical, which is why the standard doesn't try to do so. (See below.)

无论如何,c++可以由计算机解析,所以它当然可以由图灵机解析。因此,不受限制的语法可以识别它。实际上,编写这样的语法是不切实际的,这就是为什么标准不尝试这么做。(见下文)。

The issue with "ambiguity" of certain expressions is mostly a red herring. To start with, ambiguity is a feature of a particular grammar, not a language. Even if a language can be proven to have no unambiguous grammars, if it can be recognized by a context-free grammar, it's context-free. Similarly, if it cannot be recognized by a context-free grammar but it can be recognized by a context-sensitive grammar, it's context-sensitive. Ambiguity is not relevant.

某些表达的“歧义”问题主要是为了转移注意力。首先,歧义是一种特定语法的特征,而不是一种语言。即使一种语言可以被证明没有明确的语法,如果它可以被上下文无关的语法识别,那么它也是上下文无关的。类似地,如果它不能被上下文无关的语法识别,但是可以被上下文敏感的语法识别,那么它就是上下文敏感的。歧义是不相关的。

But in any event, like line 21 (i.e. auto b = foo<IsPrime<234799>>::typen<1>();) in the program below, the expressions are not ambiguous at all; they are simply parsed differently depending on context. In the simplest expression of the issue, the syntactic category of certain identifiers is dependent on how they have been declared (types and functions, for example), which means that the formal language would have to recognize the fact that two arbitrary-length strings in the same program are identical (declaration and use). This can be modelled by the "copy" grammar, which is the grammar which recognizes two consecutive exact copies of the same word. It's easy to prove with the pumping lemma that this language is not context-free. A context-sensitive grammar for this language is possible, and a Type-0 grammar is provided in the answer to this question: https://math.stackexchange.com/questions/163830/context-sensitive-grammar-for-the-copy-language .

但在任何情况下,如下面程序中的第21行(即auto b = foo >:::typen<1>(););根据上下文不同,它们的解析是不同的。最简单的表达问题,某些标识符的语法类别取决于他们是如何被宣布(例如,类型和函数),这意味着正式语言必须承认一个事实,在同一个程序中两个任意长度字符串是相同的(声明和使用)。这可以用“复制”语法来建模,语法是识别两个连续的同一个单词的精确副本的语法。用泵浦引理很容易证明这种语言不是上下文无关的。这种语言的上下文敏感语法是可能的,在这个问题的答案中提供了类型-0语法:https://math.stackexchange.com/questions/163830/上下文敏感语法-for the-copy-language。

If one were to attempt to write a context-sensitive (or unrestricted) grammar to parse C++, it would quite possibly fill the universe with scribblings. Writing a Turing machine to parse C++ would be an equally impossible undertaking. Even writing a C++ program is difficult, and as far as I know none have been proven correct. This is why the standard does not attempt to provide a complete formal grammar, and why it chooses to write some of the parsing rules in technical English.

如果有人试图编写上下文敏感的(或不受限制的)语法来解析c++,那么很可能会出现乱涂乱画的情况。编写一个图灵机来解析c++也同样不可能。即使是编写c++程序也很困难,据我所知,还没有一个程序被证明是正确的。这就是为什么标准不尝试提供完整的正式语法,以及为什么它选择用技术英语编写一些解析规则。

What looks like a formal grammar in the C++ standard is not the complete formal definition of the syntax of the C++ language. It's not even the complete formal definition of the language after preprocessing, which might be easier to formalize. (That wouldn't be the language, though: the C++ language as defined by the standard includes the preprocessor, and the operation of the preprocessor is described algorithmically since it would be extremely hard to describe in any grammatical formalism. It is in that section of the standard where lexical decomposition is described, including the rules where it must be applied more than once.)

在c++标准中看似正式的语法并不是c++语言语法的完整正式定义。它甚至不是语言经过预处理后的完整的正式定义,这可能更容易形式化。(不过,这不是语言:标准定义的c++语言包括预处理器,而预处理器的操作是通过算法来描述的,因为用任何语法形式来描述都是极其困难的。正是在该标准中描述词汇分解的部分,包括必须多次应用的规则。

The various grammars (two overlapping grammars for lexical analysis, one which takes place before preprocessing and the other, if necessary, afterwards, plus the "syntactic" grammar) are collected in Appendix A, with this important note (emphasis added):

各种语法(两个用于词汇分析的重叠语法,一个发生在预处理之前,另一个在必要时发生在预处理之后,加上“句法”语法)收集在附录A中,其中有一个重要的注释(强调部分):

This summary of C++ syntax is intended to be an aid to comprehension. It is not an exact statement of the language. In particular, the grammar described here accepts a superset of valid C++ constructs. Disambiguation rules (6.8, 7.1, 10.2) must be applied to distinguish expressions from declarations. Further, access control, ambiguity, and type rules must be used to weed out syntactically valid but meaningless constructs.

本文对c++语法的总结旨在帮助理解。这不是语言的确切表述。特别是,这里描述的语法接受有效的c++构造的超集。必须应用消除歧义规则(6.8、7.1、10.2)来区分表达式和声明。此外,访问控制、歧义和类型规则必须用于清除语法有效但毫无意义的构造。

Finally, here's the promised program. Line 21 is syntactically correct if and only if the N in IsPrime<N> is prime. Otherwise, typen is an integer, not a template, so typen<1>() is parsed as (typen<1)>() which is syntactically incorrect because () is not a syntactically valid expression.

最后,这是承诺的计划。当且仅当IsPrime 中的N为素数时,第21行在语法上是正确的。否则,类型是一个整数,而不是模板,因此类型<1>()被解析为(typen<1)>(),这在语法上是不正确的,因为()不是语法上有效的表达式。

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] To put it more technically, every production in a context-sensitive grammar must be of the form:

[1]更严格地说,上下文敏感语法中的每一个产品都必须是以下形式:

αAβ → αγβ

αAβ→αγβ

where A is a non-terminal and α, β are possibly empty sequences of grammar symbols, and γ is a non-empty sequence. (Grammar symbols may be either terminals or non-terminals).

是一个非终结符,α,β是可能为空的语法符号序列,γ是一个非空序列。(语法符号可以是终端,也可以是非终端)。

This can be read as A → γ only in the context [α, β]. In a context-free (Type 2) grammar, α and β must be empty.

这可以解读为→γ(α,β)只有在上下文。在一个上下文无关文法(2型),α和β必须是空的。

It turns out that you can also restrict grammars with the "monotonic" restriction, where every production must be of the form:

事实证明,你也可以用“单调”限制来限制语法,在这种限制下,每一个输出都必须是形式的:

α → β where |α| ≥ |β| > 0  (|α| means "the length of α")

α→β,|α|≥|β| > 0(|α|意味着“α”的长度)

It's possible to prove that the set of languages recognized by monotonic grammars is exactly the same as the set of languages recognized by context-sensitive grammars, and it's often the case that it's easier to base proofs on monotonic grammars. Consequently, it's pretty common to see "context-sensitive" used as though it meant "monotonic".

可以证明,单调语法识别出的一组语言与上下文敏感文法所识别的一组语言是完全相同的,而且通常情况下,对单调语法进行基础证明比较容易。因此,“上下文敏感”的用法非常常见,就好像它是“单调”的意思。

#2


109  

First, you rightly observed there are no context sensitive rules in the grammar at the end of the C++ standard, so that grammar is context-free.

首先,您正确地注意到,在c++标准的末尾语法中没有上下文敏感规则,因此语法是上下文无关的。

However, that grammar doesn't precisely describe the C++ language, because it produces non-C++ programs such as

但是,该语法不能精确地描述c++语言,因为它生成非c++程序,比如

int m() { m++; }

or

typedef static int int;

The C++ language defined as "the set of well-formed C++ programs" is not context-free (it's possible to show that merely demanding variables to be declared makes it so). Given you can theoretically write Turing-complete programs in templates and make a program ill-formed based on their result, it's not even context-sensitive.

定义为“格式良好的c++程序集”的c++语言不是上下文无关的(可以表明,仅仅需要声明的变量就可以做到这一点)。理论上,您可以在模板中编写Turing-complete程序,并根据它们的结果生成一个不完整的程序,因此它甚至不受上下文的影响。

Now, (ignorant) people (usually not language theorists, but parser designers) typically use "not context-free" in some of the following meanings

现在,(无知的)人们(通常不是语言理论家,而是解析器设计人员)通常在以下含义中使用“非上下文无关”

  • ambiguous
  • 模棱两可的
  • can't be parsed with Bison
  • 不能和野牛一起吃吗
  • not LL(k), LR(k), LALR(k) or whatever parser-defined language class they chose
  • 不是LL(k)、LR(k)、LALR(k)或它们选择的任何解析定义的语言类

The grammar at the back of the standard doesn't satisfy these categories (i.e. it is ambiguous, not LL(k)...) so C++ grammar is "not context-free" for them. And in a sense, they're right it's damn well hard to produce a working C++ parser.

标准后面的语法不能满足这些类别(也就是说,它是模糊的,而不是LL(k)…)所以c++语法对他们来说是“不上下文无关的”。从某种意义上说,他们是对的,生成一个有效的c++解析器非常困难。

Note that the properties here used are only weakly connected to context-free languages - ambiguity doesn't have anything to do with context-sensitivity (in fact, context-sensitive rules typically help disambiguate productions), the other two are merely subsets of context-free languages. And parsing context-free languages is not a linear process (although parsing deterministic ones is).

注意,这里使用的属性只是弱连接到上下文无关的语言——歧义与上下文敏感性没有任何关系(实际上,上下文敏感的规则通常有助于消除对结果的歧义),其他两个只是上下文无关语言的子集。解析上下文无关的语言不是一个线性过程(尽管解析确定性的语言是)。

#3


58  

Yes. The following expression has a different order of operations depending on type resolved context:

是的。下面的表达式根据类型解析上下文有不同的操作顺序:

Edit: When the actual order of operation varies, it makes it incredibly difficult to use a "regular" compiler that parses to an undecorated AST before decorating it (propagating type information). Other context sensitive things mentioned are "rather easy" compared to this (not that template evaluation is at all easy).

编辑:当实际操作顺序发生变化时,使用“常规”编译器来解析未修饰的AST(传播类型信息)是非常困难的。与此相比,提到的其他上下文敏感的内容“相当容易”(并不是说模板评估非常容易)。

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

Followed by:

紧随其后的是:

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

#4


22  

To answer your question, you need to distinguish two different questions.

要回答你的问题,你需要区分两个不同的问题。

  1. The mere syntax of almost every programming language is context-free. Typically, it is given as an extended Backus-Naur form or context-free gramar.

    几乎所有编程语言的语法都是上下文无关的。通常,它作为扩展的Backus-Naur形式或上下文无关的语法给出。

  2. However, even if a program conforms with the context-free gramar defined by the programming language, it is not necessarily a valid program. There are many non-context-free poperties that a program has to satisfy in order to be a valid program. E.g., the most simple such property is the scope of variables.

    但是,即使一个程序符合由编程语言定义的上下文无关的语法,它也不一定是一个有效的程序。为了成为一个有效的程序,程序必须满足许多非上下文无关的poperties。最简单的这类性质是变量的范围。

To conclude, whether or not C++ is context-free depends on the question you ask.

最后,无论c++是否与上下文无关,都取决于您所问的问题。

#5


10  

C++ is parsed with GLR parser. That means during parsing the source code, the parser may encounter ambiguity but it should continue and decide which grammar rule to use later.

使用GLR解析器解析c++。这意味着在解析源代码时,解析器可能会遇到歧义,但它应该继续,并决定稍后使用哪个语法规则。

look also,

还看,

Why C++ cannot be parsed with a LR(1) parser?

为什么不能用LR(1)解析器解析c++ ?


Remember that context-free grammar can not describe ALL the rules of a programming language syntax. For example, Attribute grammar is used to check the validity of an expression type.

记住,上下文无关语法不能描述编程语言语法的所有规则。例如,属性语法用于检查表达式类型的有效性。

int x;
x = 9 + 1.0;

You can not describe the following rule with context-free grammar : The Right Side of the assignment should be of the same type of the Left Hand side.

你不能用上下文无关的语法来描述以下规则:赋值的右侧应该与左侧相同。

#6


10  

Yeah C++ is context sensitive, very context sensitive. You cannot build the syntax tree by simply parsing through the file using a context free parser because in some cases you need to know the symbol from previous knowledge to decide (ie. build a symbol table while parsing).

是的,c++是上下文敏感的,非常上下文敏感的。您不能通过使用上下文无关的解析器对文件进行解析来构建语法树,因为在某些情况下,您需要从以前的知识中了解符号来决定。在解析时构建一个符号表。

First example:

第一个例子:

A*B;

Is this a multiplication expression?

这是乘法吗?

OR

Is this a declaration of B variable to be a pointer of type A?

这是一个将B变量声明为a类型指针的声明吗?

If A is a variable, then it's an expression, if A is type, it's a pointer declaration.

如果A是变量,那么它就是表达式,如果A是类型,它就是指针声明。

Second example:

第二个例子:

A B(bar);

Is this a function prototype taking an argument of bar type?

这是一个带有bar类型参数的函数原型吗?

OR

Is this declare variable B of type A and calls A's constructor with bar constant as an initializer?

这是A类型的声明变量B,并以bar常量作为初始化器调用A的构造函数吗?

You need to know again whether bar is a variable or a type from symbol table.

您需要再次了解bar是变量还是符号表中的类型。

Third example:

第三个例子:

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

This is the case when building symbol table while parsing does not help because the declaration of x and y comes after the function definition. So you need to scan through the class definition first, and look at the method definitions in a second pass, to tell x*y is an expression, and not a pointer declaration or whatever.

当构建符号表时,解析并没有帮助,因为x和y的声明是在函数定义之后出现的。因此,您需要首先浏览类定义,然后在第二遍中查看方法定义,以告诉x*y是一个表达式,而不是指针声明之类的。

#7


10  

You might want to take a look at The Design & Evolution of C++, by Bjarne Stroustrup. In it he describes his problems trying to use yacc (or similar) to parse an early version of C++, and wishing he had used recursive descent instead.

您可能想看看Bjarne Stroustrup所著的c++的设计与演变。在文中,他描述了自己在使用yacc(或类似的)解析早期c++版本时遇到的问题,并希望使用递归下降来代替。

#8


8  

I have a feeling that there's some confusion between the formal definition of "context-sensitive" and the informal use of "context-sensitive". The former has a well-defined meaning. The latter is used for saying "you need context in order to parse the input".

我有一种感觉,在“上下文敏感”的正式定义和“上下文敏感”的非正式用法之间存在一些混淆。前者有明确的含义。后者用于表示“您需要上下文来解析输入”。

This is also asked here: Context-sensitivity vs Ambiguity.

这里也有一个问题:语境敏感性和模糊性。

Here's a context-free grammar:

这里有一个上下文无关的语法:

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

It's ambiguous, so in order to parse the input "x" you need some context (or live with the ambiguity, or emit "Warning: E8271 - Input is ambiguous in line 115"). But it's certainly not a context-sensitive grammar.

它是不明确的,所以为了解析输入“x”,您需要一些上下文(或者使用模糊性,或者发出“警告:E8271—输入在第115行中是不明确的”)。但它肯定不是上下文敏感的语法。

#9


5  

No Algol-like language is context-free, because they have rules that constrain expressions and statements that identifiers can appear in based on their type, and because there's no limit on the number of statements that can occur between declaration and use.

没有一种类似algolas的语言是上下文无关的,因为它们有约束表达式和语句的规则,标识符可以根据它们的类型出现在这些语句中,并且因为声明和使用之间可以发生的语句的数量没有限制。

The usual solution is to write a context-free parser that actually accepts a superset of valid programs and put the context-sensitive portions in ad hoc "semantic" code attached to rules.

通常的解决方案是编写一个上下文无关的解析器,该解析器实际上接受一个有效程序的超集,并将上下文敏感的部分放在附加到规则的特定“语义”代码中。

C++ goes well beyond this, thanks to its Turing-complete template system. See Stack Overflow Question 794015.

由于它的Turing-complete模板系统,c++远远超出了这个范围。参见堆栈溢出问题794015。

#10


5  

The simplest case of non-context-free grammar involves parsing expressions involving templates.

非上下文无关语法的最简单情况涉及解析表达式,涉及到模板。

a<b<c>()

This can parse as either

这也可以解析为。

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

Or

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

The two ASTs can only be disambiguated by examining the declaration of 'a' -- the former AST if 'a' is a template, or the latter if not.

两个AST只能通过检查'a'的声明来消除歧义——前者如果'a'是模板,后者如果不是模板,则前者。

#11


4  

True :)

真正的:)

J. Stanley Warford. Computer systems. Pages 341-346.

j·斯坦利Warford。计算机系统。页341 - 346。

#12


4  

It is context-sensitive, as a b(c); has two valid parses- declaration and variable. When you say "If c is a type", that's context, right there, and you've described exactly how C++ is sensitive to it. If you didn't have that context of "What is c?" you could not parse this unambiguously.

它是上下文敏感的,如b(c);有两个有效的解析——声明和变量。当你说“如果c是一种类型”时,这就是上下文,就在这里,你已经描述了c++对它的敏感程度。如果您没有“c是什么?”的上下文,您就无法明确地解析它。

Here, the context is expressed in the choice of tokens- the parser reads an identifier as a typename token if it names a type. This is the simplest resolution, and avoids much of the complexity of being context-sensitive (in this case).

在这里,上下文是在令牌的选择中表示的——解析器将一个标识符作为一个typename标记,如果它命名一个类型的话。这是最简单的解决方案,避免了上下文敏感(在本例中)的复杂性。

Edit: There are, of course, more issues of context sensitivity, I have merely focused on the one you've shown. Templates are especially nasty for this.

编辑:当然,还有更多的上下文敏感性问题,我只关注你展示的那个。模板对此特别讨厌。

#13


4  

The productions in the C++ standard are written context-free, but as we all know don't really define the language precisely. Some of what most people see as ambiguity in the current language could (I believe) be resolved unambiguously with a context sensitive grammar.

c++标准中的产品是不受上下文限制的,但是我们都知道并不能准确地定义语言。在大多数人看来,当前语言中的一些歧义(我认为)可以通过上下文敏感的语法明确地解决。

For the most obvious example, let's consider the Most Vexing Parse: int f(X);. If X is a value, then this defines f as a variable that will be initialized with X. If X is a type, it defines f as a function taking a single parameter of type X.

对于最明显的示例,让我们考虑最烦人的解析:int f(X);如果X是一个值,那么它将f定义为一个将用X初始化的变量。

Looking at that from a grammatical viewpoint, we could view it like this:

从语法的角度来看,我们可以这样看

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]

Of course, to be entirely correct we'd need to add some extra "stuff" to account for the possibility of intervening declarations of other types (i.e., A and B should both really be "declarations including declaration of X as...", or something on that order).

当然,为了完全正确,我们需要添加一些额外的“东西”,以解释干涉其他类型声明的可能性。A和B都应该是“声明,包括X的声明…”,或类似的东西)。

This is still rather different from a typical CSG though (or at least what I recall of them). This depends on a symbol table being constructed -- the part that specifically recognizes X as a type or value, not just some type of statement preceding this, but the correct type of statement for the right symbol/identifier.

这与典型的CSG仍然有很大的不同(至少我记得是这样)。这取决于正在构造的符号表——特定地将X识别为类型或值的部分,不只是前面的某种类型的语句,而是正确的符号/标识符的正确类型的语句。

As such, I'd have to do some looking to be sure, but my immediate guess is that this doesn't really qualify as a CSG, at least as the term is normally used.

因此,我必须做一些确定的事情,但我的直接猜测是,这并不真正符合CSG的标准,至少在通常使用的术语中是这样。

#14


3  

Sometimes it's worse: What do people mean when they say C++ has "undecidable grammar"?

有时情况更糟:当人们说c++有“不可决定语法”时,是什么意思?

#15


3  

C++ templates have been shown to be Turing Powerful. Although not a formal reference, here's a place to look in that regard:

c++模板显示出强大的图灵功能。虽然不是一个正式的参考,这里有一个地方可以参考:

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

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

I will venture a guess (as old as a folkoric and concise CACM proof showing that ALGOL in the 60's could not be reprsented by a CFG) and say that C++ cannot therefore be correctly parsed only by a CFG. CFGs, in conjunction with various TP mechanisms in either a tree pass or during reduction events -- this is another story. In a general sense, due to the Halting Problem, there exists some C++ program that cannot be shown to be correct/incorrect but is nonetheless correct/incorrect.

我将大胆地猜测(就像一个古老的、简明的CACM证明表明60年代的ALGOL不能用CFG表示),并说只有CFG才能正确地解析c++。CFGs与树传递或还原事件中的各种TP机制结合在一起——这是另一回事。一般来说,由于停止问题,存在一些c++程序不能显示为正确/不正确,但仍然是正确/不正确的。

{PS- As the author of Meta-S (mentioned by several people above) -- I can most assuredly say that Thothic is neither defunct, nor is the software available for free. Perhaps I have worded this version of my response such that I do not get deleted or voted down to -3.}

{PS- --作为Meta-S的作者(上面提到的几个人)-我可以肯定地说,这些都不是不存在的,也不是免费的软件。也许我已经写了这个版本的回复,这样我就不会被删除或投票给-3}。

#16


2  

C++ is not context free. I learned it some time ago in compilers lecture. A quick search gave this link, where the "Syntax or semantics" section explains why C and C++ are not context free:

c++不是上下文无关的。我在编译课上学过。快速的搜索就给出了这个链接,其中的“语法或语义”部分解释了为什么C和c++不是上下文无关的:

Wikipedia Talk: Context-Free grammar

*说:上下文无关语法

Regards,
Ovanes

问候,Ovanes

#17


1  

Obviously, if you take the question verbatim, nearly all languages with identifiers are context sensitive.

显然,如果逐字处理这个问题,几乎所有带有标识符的语言都是上下文敏感的。

One need to know if an identifier is a type name (a class name, a name introduced by typedef, a typename template parameter), a template name or some other name to be able to correctly some of the use of identifier. For instance:

需要知道标识符是类型名(类名、typedef引入的名称、typename模板参数)、模板名还是其他名称,才能正确地使用标识符。例如:

x = (name)(expression);

is a cast if name is a type name and a function call if name is a function name. Another case is the so called "most vexing parse" where it isn't possible to differentiate variable definition and function declaration (there is a rule saying it is a function declaration).

如果名称是类型名,则为类型转换;如果名称是函数名,则为函数调用。另一种情况是所谓的“最烦人的解析”,即不可能区分变量定义和函数声明(有一条规则说它是函数声明)。

That difficulty has introduced the need of typename and template with dependent names. The rest of C++ isn't context sensitive as far as I know (i.e. it is possible to write a context free grammar for it).

这一困难引入了typename和带有依赖名称的模板的需求。就我所知,c++的其余部分对上下文不敏感(也就是说,可以为它编写一个上下文无关的语法)。

#18


1  

Meta-S" is a context-sensitive parsing engine by Quinn Tyler Jackson. I've not used it, but he tells an impressive story. Check out his comments in comp.compilers, and see rnaparse.com/MetaS%20defined.htm – Ira Baxter Jul 25 at 10:42

Meta-S是Quinn Tyler Jackson设计的上下文敏感的解析引擎。我还没用过,但他讲了一个令人印象深刻的故事。查看他在编译器上的评论,见rnaparse.com/metas%20defin. htm - Ira Baxter Jul 25 at 10:42

The correct link is parsing enigines

正确的链接是解析谜

Meta-S was the property of a defunct company called Thothic. I can send a free copy of the Meta-S to anyone interested and I've used it in rna parsing research. Please note the "pseudoknot grammar" included in the examples folders was written by an non-bioinformatics, amature programmer and basically doesn't work. My grammars take a different approach and work quite well.

Meta-S是一家已经倒闭的公司的财产,该公司名为thothought。我可以向任何感兴趣的人发送免费的元数据拷贝,我已经在rna解析研究中使用过它。请注意,示例文件夹中包含的“伪结语法”是由非生物信息学、amature程序员编写的,基本不起作用。我的语法采用了一种不同的方法,并且工作得很好。

#19


-4  

This answer says C++ is not context-free... there's an implication (not by the answerer) that it can't be parsed, and the answer offers a difficult code example which produces an invalid C++ program if a certain constant is not a prime number.

这个回答说c++不是上下文无关的……这里有一个含义(不是回答者),它不能被解析,答案提供了一个复杂的代码示例,如果某个常数不是质数,它将生成一个无效的c++程序。

As others have observed, the question about whether the language is context-sensitive/free is different than the same question about a specific grammar.

正如其他人所观察到的,关于语言是否是上下文敏感/*的问题与关于特定语法的相同问题是不同的。

To set the question about parseability to rest, I offer empirical evidence that there are context-free grammars for C++, that can be used to produce an AST for a context-free parse of the source text by in fact parsing it with an existing GLR-parser-based tool that is driven by an explicit grammar.

parseability设置问题,我提供经验证据是c++的上下文无关文法,可以用来产生一个AST源文本的上下文无关的解析与现有GLR-parser-based实际上解析工具,是由一个显式的语法。

Yes, it succeeds by "accepting too much"; not everything it accepts is a valid C++ program, which is why it's followed up with additional checks (type checks). And yes, the type checker may run into computability problems. In practice tools don't have this problem; if people wrote programs like that, none of them would compile. (I think the standard actually puts a limit on the amount of computation you can do unfolding a template, so in fact the computation is actually finite but probably pretty big).

是的,它的成功在于“接受太多”;它接受的并不是所有东西都是有效的c++程序,这就是为什么它要进行额外的检查(类型检查)。是的,类型检查器可能会遇到可计算性问题。实际上工具没有这个问题;如果人们写这样的程序,没有人会编译。(我认为标准实际上限制了展开模板的计算量,所以实际上计算是有限的,但可能相当大)。

If what you mean is, determine if the source program is a member of the set of valid C++ source programs, then I will agree the problem is a lot harder. But it isn't parsing that is the problem.

如果您的意思是,确定源程序是否为有效c++源程序集的成员,那么我将同意这个问题要困难得多。但问题不在于解析。

The tool resolves this issue by isolating parsing from type-checking the parsed program. (Where there are multiple interpretations in the absence of context, it records an ambiguity node in the parse tree with several possible parses; the type checking decides which one is correct and eliminates the invalid subtrees). You can see a (partial) parse tree in the example below; the whole tree is too large to fit in an SO answer. Note you get a parse tree whether the value 234797 or 234799 is used.

该工具通过将解析与已解析程序的类型检查隔离开来来解决这个问题。(如果在没有上下文的情况下有多种解释,那么它将使用几种可能的解析来记录解析树中的歧义节点;类型检查决定哪个是正确的,并消除无效的子树)。您可以在下面的示例中看到(部分)解析树;整棵树太大了,不适合这么回答。注意,您将得到一个解析树,它是否使用了值234797或234799。

Running the tool's name/type resolver over the AST with the original value 234799 succeeds. With the value 234797 the name resolver fails (as expected) with the error message, "typen is not a type." and thus that version is not a valid C++ program.

使用原始值234799在AST上运行工具的名称/类型解析器将成功。对于值234797,名称解析器(如预期的那样)在错误消息中失败,“类型不是类型”,因此该版本不是有效的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

#1


308  

Below is my (current) favorite demonstration of why parsing C++ is (probably) Turing-complete, since it shows a program which is syntactically correct if and only if a given integer is prime.

下面是我(当前)最喜欢的关于解析c++为什么(可能)是Turing-complete的演示,因为它显示了一个在给定整数为素数时语法正确的程序。

So I assert that C++ is neither context-free nor context-sensitive.

因此,我断言c++既不是上下文无关的,也不是上下文敏感的。

If you allow arbitrary symbol sequences on both sides of any production, you produce an Type-0 grammar ("unrestricted") in the Chomsky hierarchy, which is more powerful than a context-sensitive grammar; unrestricted grammars are Turing-complete. A context-sensitive (Type-1) grammar allows multiple symbols of context on the left hand side of a production, but the same context must appear on the right hand side of the production (hence the name "context-sensitive"). [1] Context-sensitive grammars are equivalent to linear-bounded Turing machines.

如果您允许任意符号序列出现在任何产品的两端,那么您将在乔姆斯基层次结构中生成类型-0语法(“无限制”),它比上下文敏感的语法更强大;无限制的语法是图灵完备的。上下文敏感(Type-1)语法允许在生产的左手端有多个上下文符号,但是相同的上下文必须出现在生产的右手端(因此命名为“上下文敏感”)。[1]上下文敏感文法相当于线性有界图灵机。

In the example program, the prime computation could be performed by a linear-bounded Turing machine, so it does not quite prove Turing equivalence, but the important part is that the parser needs to perform the computation in order to perform syntactic analysis. It could have been any computation expressible as a template instantiation and there is every reason to believe that C++ template instantiation is Turing-complete. See, for example, Todd L. Veldhuizen's 2003 paper.

在示例程序中,主计算可以由线性有界的图灵机来执行,因此它并不完全证明图灵等价,但重要的是解析器需要执行计算,以执行句法分析。它可以是任何可表示为模板实例化的计算,并且有充分的理由相信c++模板实例化是Turing-complete的。比如,托德·l·维尔德休岑(Todd L. Veldhuizen) 2003年的论文。

Regardless, C++ can be parsed by a computer, so it could certainly be parsed by a Turing machine. Consequently, an unrestricted grammar could recognize it. Actually writing such a grammar would be impractical, which is why the standard doesn't try to do so. (See below.)

无论如何,c++可以由计算机解析,所以它当然可以由图灵机解析。因此,不受限制的语法可以识别它。实际上,编写这样的语法是不切实际的,这就是为什么标准不尝试这么做。(见下文)。

The issue with "ambiguity" of certain expressions is mostly a red herring. To start with, ambiguity is a feature of a particular grammar, not a language. Even if a language can be proven to have no unambiguous grammars, if it can be recognized by a context-free grammar, it's context-free. Similarly, if it cannot be recognized by a context-free grammar but it can be recognized by a context-sensitive grammar, it's context-sensitive. Ambiguity is not relevant.

某些表达的“歧义”问题主要是为了转移注意力。首先,歧义是一种特定语法的特征,而不是一种语言。即使一种语言可以被证明没有明确的语法,如果它可以被上下文无关的语法识别,那么它也是上下文无关的。类似地,如果它不能被上下文无关的语法识别,但是可以被上下文敏感的语法识别,那么它就是上下文敏感的。歧义是不相关的。

But in any event, like line 21 (i.e. auto b = foo<IsPrime<234799>>::typen<1>();) in the program below, the expressions are not ambiguous at all; they are simply parsed differently depending on context. In the simplest expression of the issue, the syntactic category of certain identifiers is dependent on how they have been declared (types and functions, for example), which means that the formal language would have to recognize the fact that two arbitrary-length strings in the same program are identical (declaration and use). This can be modelled by the "copy" grammar, which is the grammar which recognizes two consecutive exact copies of the same word. It's easy to prove with the pumping lemma that this language is not context-free. A context-sensitive grammar for this language is possible, and a Type-0 grammar is provided in the answer to this question: https://math.stackexchange.com/questions/163830/context-sensitive-grammar-for-the-copy-language .

但在任何情况下,如下面程序中的第21行(即auto b = foo >:::typen<1>(););根据上下文不同,它们的解析是不同的。最简单的表达问题,某些标识符的语法类别取决于他们是如何被宣布(例如,类型和函数),这意味着正式语言必须承认一个事实,在同一个程序中两个任意长度字符串是相同的(声明和使用)。这可以用“复制”语法来建模,语法是识别两个连续的同一个单词的精确副本的语法。用泵浦引理很容易证明这种语言不是上下文无关的。这种语言的上下文敏感语法是可能的,在这个问题的答案中提供了类型-0语法:https://math.stackexchange.com/questions/163830/上下文敏感语法-for the-copy-language。

If one were to attempt to write a context-sensitive (or unrestricted) grammar to parse C++, it would quite possibly fill the universe with scribblings. Writing a Turing machine to parse C++ would be an equally impossible undertaking. Even writing a C++ program is difficult, and as far as I know none have been proven correct. This is why the standard does not attempt to provide a complete formal grammar, and why it chooses to write some of the parsing rules in technical English.

如果有人试图编写上下文敏感的(或不受限制的)语法来解析c++,那么很可能会出现乱涂乱画的情况。编写一个图灵机来解析c++也同样不可能。即使是编写c++程序也很困难,据我所知,还没有一个程序被证明是正确的。这就是为什么标准不尝试提供完整的正式语法,以及为什么它选择用技术英语编写一些解析规则。

What looks like a formal grammar in the C++ standard is not the complete formal definition of the syntax of the C++ language. It's not even the complete formal definition of the language after preprocessing, which might be easier to formalize. (That wouldn't be the language, though: the C++ language as defined by the standard includes the preprocessor, and the operation of the preprocessor is described algorithmically since it would be extremely hard to describe in any grammatical formalism. It is in that section of the standard where lexical decomposition is described, including the rules where it must be applied more than once.)

在c++标准中看似正式的语法并不是c++语言语法的完整正式定义。它甚至不是语言经过预处理后的完整的正式定义,这可能更容易形式化。(不过,这不是语言:标准定义的c++语言包括预处理器,而预处理器的操作是通过算法来描述的,因为用任何语法形式来描述都是极其困难的。正是在该标准中描述词汇分解的部分,包括必须多次应用的规则。

The various grammars (two overlapping grammars for lexical analysis, one which takes place before preprocessing and the other, if necessary, afterwards, plus the "syntactic" grammar) are collected in Appendix A, with this important note (emphasis added):

各种语法(两个用于词汇分析的重叠语法,一个发生在预处理之前,另一个在必要时发生在预处理之后,加上“句法”语法)收集在附录A中,其中有一个重要的注释(强调部分):

This summary of C++ syntax is intended to be an aid to comprehension. It is not an exact statement of the language. In particular, the grammar described here accepts a superset of valid C++ constructs. Disambiguation rules (6.8, 7.1, 10.2) must be applied to distinguish expressions from declarations. Further, access control, ambiguity, and type rules must be used to weed out syntactically valid but meaningless constructs.

本文对c++语法的总结旨在帮助理解。这不是语言的确切表述。特别是,这里描述的语法接受有效的c++构造的超集。必须应用消除歧义规则(6.8、7.1、10.2)来区分表达式和声明。此外,访问控制、歧义和类型规则必须用于清除语法有效但毫无意义的构造。

Finally, here's the promised program. Line 21 is syntactically correct if and only if the N in IsPrime<N> is prime. Otherwise, typen is an integer, not a template, so typen<1>() is parsed as (typen<1)>() which is syntactically incorrect because () is not a syntactically valid expression.

最后,这是承诺的计划。当且仅当IsPrime 中的N为素数时,第21行在语法上是正确的。否则,类型是一个整数,而不是模板,因此类型<1>()被解析为(typen<1)>(),这在语法上是不正确的,因为()不是语法上有效的表达式。

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] To put it more technically, every production in a context-sensitive grammar must be of the form:

[1]更严格地说,上下文敏感语法中的每一个产品都必须是以下形式:

αAβ → αγβ

αAβ→αγβ

where A is a non-terminal and α, β are possibly empty sequences of grammar symbols, and γ is a non-empty sequence. (Grammar symbols may be either terminals or non-terminals).

是一个非终结符,α,β是可能为空的语法符号序列,γ是一个非空序列。(语法符号可以是终端,也可以是非终端)。

This can be read as A → γ only in the context [α, β]. In a context-free (Type 2) grammar, α and β must be empty.

这可以解读为→γ(α,β)只有在上下文。在一个上下文无关文法(2型),α和β必须是空的。

It turns out that you can also restrict grammars with the "monotonic" restriction, where every production must be of the form:

事实证明,你也可以用“单调”限制来限制语法,在这种限制下,每一个输出都必须是形式的:

α → β where |α| ≥ |β| > 0  (|α| means "the length of α")

α→β,|α|≥|β| > 0(|α|意味着“α”的长度)

It's possible to prove that the set of languages recognized by monotonic grammars is exactly the same as the set of languages recognized by context-sensitive grammars, and it's often the case that it's easier to base proofs on monotonic grammars. Consequently, it's pretty common to see "context-sensitive" used as though it meant "monotonic".

可以证明,单调语法识别出的一组语言与上下文敏感文法所识别的一组语言是完全相同的,而且通常情况下,对单调语法进行基础证明比较容易。因此,“上下文敏感”的用法非常常见,就好像它是“单调”的意思。

#2


109  

First, you rightly observed there are no context sensitive rules in the grammar at the end of the C++ standard, so that grammar is context-free.

首先,您正确地注意到,在c++标准的末尾语法中没有上下文敏感规则,因此语法是上下文无关的。

However, that grammar doesn't precisely describe the C++ language, because it produces non-C++ programs such as

但是,该语法不能精确地描述c++语言,因为它生成非c++程序,比如

int m() { m++; }

or

typedef static int int;

The C++ language defined as "the set of well-formed C++ programs" is not context-free (it's possible to show that merely demanding variables to be declared makes it so). Given you can theoretically write Turing-complete programs in templates and make a program ill-formed based on their result, it's not even context-sensitive.

定义为“格式良好的c++程序集”的c++语言不是上下文无关的(可以表明,仅仅需要声明的变量就可以做到这一点)。理论上,您可以在模板中编写Turing-complete程序,并根据它们的结果生成一个不完整的程序,因此它甚至不受上下文的影响。

Now, (ignorant) people (usually not language theorists, but parser designers) typically use "not context-free" in some of the following meanings

现在,(无知的)人们(通常不是语言理论家,而是解析器设计人员)通常在以下含义中使用“非上下文无关”

  • ambiguous
  • 模棱两可的
  • can't be parsed with Bison
  • 不能和野牛一起吃吗
  • not LL(k), LR(k), LALR(k) or whatever parser-defined language class they chose
  • 不是LL(k)、LR(k)、LALR(k)或它们选择的任何解析定义的语言类

The grammar at the back of the standard doesn't satisfy these categories (i.e. it is ambiguous, not LL(k)...) so C++ grammar is "not context-free" for them. And in a sense, they're right it's damn well hard to produce a working C++ parser.

标准后面的语法不能满足这些类别(也就是说,它是模糊的,而不是LL(k)…)所以c++语法对他们来说是“不上下文无关的”。从某种意义上说,他们是对的,生成一个有效的c++解析器非常困难。

Note that the properties here used are only weakly connected to context-free languages - ambiguity doesn't have anything to do with context-sensitivity (in fact, context-sensitive rules typically help disambiguate productions), the other two are merely subsets of context-free languages. And parsing context-free languages is not a linear process (although parsing deterministic ones is).

注意,这里使用的属性只是弱连接到上下文无关的语言——歧义与上下文敏感性没有任何关系(实际上,上下文敏感的规则通常有助于消除对结果的歧义),其他两个只是上下文无关语言的子集。解析上下文无关的语言不是一个线性过程(尽管解析确定性的语言是)。

#3


58  

Yes. The following expression has a different order of operations depending on type resolved context:

是的。下面的表达式根据类型解析上下文有不同的操作顺序:

Edit: When the actual order of operation varies, it makes it incredibly difficult to use a "regular" compiler that parses to an undecorated AST before decorating it (propagating type information). Other context sensitive things mentioned are "rather easy" compared to this (not that template evaluation is at all easy).

编辑:当实际操作顺序发生变化时,使用“常规”编译器来解析未修饰的AST(传播类型信息)是非常困难的。与此相比,提到的其他上下文敏感的内容“相当容易”(并不是说模板评估非常容易)。

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

Followed by:

紧随其后的是:

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

#4


22  

To answer your question, you need to distinguish two different questions.

要回答你的问题,你需要区分两个不同的问题。

  1. The mere syntax of almost every programming language is context-free. Typically, it is given as an extended Backus-Naur form or context-free gramar.

    几乎所有编程语言的语法都是上下文无关的。通常,它作为扩展的Backus-Naur形式或上下文无关的语法给出。

  2. However, even if a program conforms with the context-free gramar defined by the programming language, it is not necessarily a valid program. There are many non-context-free poperties that a program has to satisfy in order to be a valid program. E.g., the most simple such property is the scope of variables.

    但是,即使一个程序符合由编程语言定义的上下文无关的语法,它也不一定是一个有效的程序。为了成为一个有效的程序,程序必须满足许多非上下文无关的poperties。最简单的这类性质是变量的范围。

To conclude, whether or not C++ is context-free depends on the question you ask.

最后,无论c++是否与上下文无关,都取决于您所问的问题。

#5


10  

C++ is parsed with GLR parser. That means during parsing the source code, the parser may encounter ambiguity but it should continue and decide which grammar rule to use later.

使用GLR解析器解析c++。这意味着在解析源代码时,解析器可能会遇到歧义,但它应该继续,并决定稍后使用哪个语法规则。

look also,

还看,

Why C++ cannot be parsed with a LR(1) parser?

为什么不能用LR(1)解析器解析c++ ?


Remember that context-free grammar can not describe ALL the rules of a programming language syntax. For example, Attribute grammar is used to check the validity of an expression type.

记住,上下文无关语法不能描述编程语言语法的所有规则。例如,属性语法用于检查表达式类型的有效性。

int x;
x = 9 + 1.0;

You can not describe the following rule with context-free grammar : The Right Side of the assignment should be of the same type of the Left Hand side.

你不能用上下文无关的语法来描述以下规则:赋值的右侧应该与左侧相同。

#6


10  

Yeah C++ is context sensitive, very context sensitive. You cannot build the syntax tree by simply parsing through the file using a context free parser because in some cases you need to know the symbol from previous knowledge to decide (ie. build a symbol table while parsing).

是的,c++是上下文敏感的,非常上下文敏感的。您不能通过使用上下文无关的解析器对文件进行解析来构建语法树,因为在某些情况下,您需要从以前的知识中了解符号来决定。在解析时构建一个符号表。

First example:

第一个例子:

A*B;

Is this a multiplication expression?

这是乘法吗?

OR

Is this a declaration of B variable to be a pointer of type A?

这是一个将B变量声明为a类型指针的声明吗?

If A is a variable, then it's an expression, if A is type, it's a pointer declaration.

如果A是变量,那么它就是表达式,如果A是类型,它就是指针声明。

Second example:

第二个例子:

A B(bar);

Is this a function prototype taking an argument of bar type?

这是一个带有bar类型参数的函数原型吗?

OR

Is this declare variable B of type A and calls A's constructor with bar constant as an initializer?

这是A类型的声明变量B,并以bar常量作为初始化器调用A的构造函数吗?

You need to know again whether bar is a variable or a type from symbol table.

您需要再次了解bar是变量还是符号表中的类型。

Third example:

第三个例子:

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

This is the case when building symbol table while parsing does not help because the declaration of x and y comes after the function definition. So you need to scan through the class definition first, and look at the method definitions in a second pass, to tell x*y is an expression, and not a pointer declaration or whatever.

当构建符号表时,解析并没有帮助,因为x和y的声明是在函数定义之后出现的。因此,您需要首先浏览类定义,然后在第二遍中查看方法定义,以告诉x*y是一个表达式,而不是指针声明之类的。

#7


10  

You might want to take a look at The Design & Evolution of C++, by Bjarne Stroustrup. In it he describes his problems trying to use yacc (or similar) to parse an early version of C++, and wishing he had used recursive descent instead.

您可能想看看Bjarne Stroustrup所著的c++的设计与演变。在文中,他描述了自己在使用yacc(或类似的)解析早期c++版本时遇到的问题,并希望使用递归下降来代替。

#8


8  

I have a feeling that there's some confusion between the formal definition of "context-sensitive" and the informal use of "context-sensitive". The former has a well-defined meaning. The latter is used for saying "you need context in order to parse the input".

我有一种感觉,在“上下文敏感”的正式定义和“上下文敏感”的非正式用法之间存在一些混淆。前者有明确的含义。后者用于表示“您需要上下文来解析输入”。

This is also asked here: Context-sensitivity vs Ambiguity.

这里也有一个问题:语境敏感性和模糊性。

Here's a context-free grammar:

这里有一个上下文无关的语法:

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

It's ambiguous, so in order to parse the input "x" you need some context (or live with the ambiguity, or emit "Warning: E8271 - Input is ambiguous in line 115"). But it's certainly not a context-sensitive grammar.

它是不明确的,所以为了解析输入“x”,您需要一些上下文(或者使用模糊性,或者发出“警告:E8271—输入在第115行中是不明确的”)。但它肯定不是上下文敏感的语法。

#9


5  

No Algol-like language is context-free, because they have rules that constrain expressions and statements that identifiers can appear in based on their type, and because there's no limit on the number of statements that can occur between declaration and use.

没有一种类似algolas的语言是上下文无关的,因为它们有约束表达式和语句的规则,标识符可以根据它们的类型出现在这些语句中,并且因为声明和使用之间可以发生的语句的数量没有限制。

The usual solution is to write a context-free parser that actually accepts a superset of valid programs and put the context-sensitive portions in ad hoc "semantic" code attached to rules.

通常的解决方案是编写一个上下文无关的解析器,该解析器实际上接受一个有效程序的超集,并将上下文敏感的部分放在附加到规则的特定“语义”代码中。

C++ goes well beyond this, thanks to its Turing-complete template system. See Stack Overflow Question 794015.

由于它的Turing-complete模板系统,c++远远超出了这个范围。参见堆栈溢出问题794015。

#10


5  

The simplest case of non-context-free grammar involves parsing expressions involving templates.

非上下文无关语法的最简单情况涉及解析表达式,涉及到模板。

a<b<c>()

This can parse as either

这也可以解析为。

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

Or

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

The two ASTs can only be disambiguated by examining the declaration of 'a' -- the former AST if 'a' is a template, or the latter if not.

两个AST只能通过检查'a'的声明来消除歧义——前者如果'a'是模板,后者如果不是模板,则前者。

#11


4  

True :)

真正的:)

J. Stanley Warford. Computer systems. Pages 341-346.

j·斯坦利Warford。计算机系统。页341 - 346。

#12


4  

It is context-sensitive, as a b(c); has two valid parses- declaration and variable. When you say "If c is a type", that's context, right there, and you've described exactly how C++ is sensitive to it. If you didn't have that context of "What is c?" you could not parse this unambiguously.

它是上下文敏感的,如b(c);有两个有效的解析——声明和变量。当你说“如果c是一种类型”时,这就是上下文,就在这里,你已经描述了c++对它的敏感程度。如果您没有“c是什么?”的上下文,您就无法明确地解析它。

Here, the context is expressed in the choice of tokens- the parser reads an identifier as a typename token if it names a type. This is the simplest resolution, and avoids much of the complexity of being context-sensitive (in this case).

在这里,上下文是在令牌的选择中表示的——解析器将一个标识符作为一个typename标记,如果它命名一个类型的话。这是最简单的解决方案,避免了上下文敏感(在本例中)的复杂性。

Edit: There are, of course, more issues of context sensitivity, I have merely focused on the one you've shown. Templates are especially nasty for this.

编辑:当然,还有更多的上下文敏感性问题,我只关注你展示的那个。模板对此特别讨厌。

#13


4  

The productions in the C++ standard are written context-free, but as we all know don't really define the language precisely. Some of what most people see as ambiguity in the current language could (I believe) be resolved unambiguously with a context sensitive grammar.

c++标准中的产品是不受上下文限制的,但是我们都知道并不能准确地定义语言。在大多数人看来,当前语言中的一些歧义(我认为)可以通过上下文敏感的语法明确地解决。

For the most obvious example, let's consider the Most Vexing Parse: int f(X);. If X is a value, then this defines f as a variable that will be initialized with X. If X is a type, it defines f as a function taking a single parameter of type X.

对于最明显的示例,让我们考虑最烦人的解析:int f(X);如果X是一个值,那么它将f定义为一个将用X初始化的变量。

Looking at that from a grammatical viewpoint, we could view it like this:

从语法的角度来看,我们可以这样看

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]

Of course, to be entirely correct we'd need to add some extra "stuff" to account for the possibility of intervening declarations of other types (i.e., A and B should both really be "declarations including declaration of X as...", or something on that order).

当然,为了完全正确,我们需要添加一些额外的“东西”,以解释干涉其他类型声明的可能性。A和B都应该是“声明,包括X的声明…”,或类似的东西)。

This is still rather different from a typical CSG though (or at least what I recall of them). This depends on a symbol table being constructed -- the part that specifically recognizes X as a type or value, not just some type of statement preceding this, but the correct type of statement for the right symbol/identifier.

这与典型的CSG仍然有很大的不同(至少我记得是这样)。这取决于正在构造的符号表——特定地将X识别为类型或值的部分,不只是前面的某种类型的语句,而是正确的符号/标识符的正确类型的语句。

As such, I'd have to do some looking to be sure, but my immediate guess is that this doesn't really qualify as a CSG, at least as the term is normally used.

因此,我必须做一些确定的事情,但我的直接猜测是,这并不真正符合CSG的标准,至少在通常使用的术语中是这样。

#14


3  

Sometimes it's worse: What do people mean when they say C++ has "undecidable grammar"?

有时情况更糟:当人们说c++有“不可决定语法”时,是什么意思?

#15


3  

C++ templates have been shown to be Turing Powerful. Although not a formal reference, here's a place to look in that regard:

c++模板显示出强大的图灵功能。虽然不是一个正式的参考,这里有一个地方可以参考:

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

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

I will venture a guess (as old as a folkoric and concise CACM proof showing that ALGOL in the 60's could not be reprsented by a CFG) and say that C++ cannot therefore be correctly parsed only by a CFG. CFGs, in conjunction with various TP mechanisms in either a tree pass or during reduction events -- this is another story. In a general sense, due to the Halting Problem, there exists some C++ program that cannot be shown to be correct/incorrect but is nonetheless correct/incorrect.

我将大胆地猜测(就像一个古老的、简明的CACM证明表明60年代的ALGOL不能用CFG表示),并说只有CFG才能正确地解析c++。CFGs与树传递或还原事件中的各种TP机制结合在一起——这是另一回事。一般来说,由于停止问题,存在一些c++程序不能显示为正确/不正确,但仍然是正确/不正确的。

{PS- As the author of Meta-S (mentioned by several people above) -- I can most assuredly say that Thothic is neither defunct, nor is the software available for free. Perhaps I have worded this version of my response such that I do not get deleted or voted down to -3.}

{PS- --作为Meta-S的作者(上面提到的几个人)-我可以肯定地说,这些都不是不存在的,也不是免费的软件。也许我已经写了这个版本的回复,这样我就不会被删除或投票给-3}。

#16


2  

C++ is not context free. I learned it some time ago in compilers lecture. A quick search gave this link, where the "Syntax or semantics" section explains why C and C++ are not context free:

c++不是上下文无关的。我在编译课上学过。快速的搜索就给出了这个链接,其中的“语法或语义”部分解释了为什么C和c++不是上下文无关的:

Wikipedia Talk: Context-Free grammar

*说:上下文无关语法

Regards,
Ovanes

问候,Ovanes

#17


1  

Obviously, if you take the question verbatim, nearly all languages with identifiers are context sensitive.

显然,如果逐字处理这个问题,几乎所有带有标识符的语言都是上下文敏感的。

One need to know if an identifier is a type name (a class name, a name introduced by typedef, a typename template parameter), a template name or some other name to be able to correctly some of the use of identifier. For instance:

需要知道标识符是类型名(类名、typedef引入的名称、typename模板参数)、模板名还是其他名称,才能正确地使用标识符。例如:

x = (name)(expression);

is a cast if name is a type name and a function call if name is a function name. Another case is the so called "most vexing parse" where it isn't possible to differentiate variable definition and function declaration (there is a rule saying it is a function declaration).

如果名称是类型名,则为类型转换;如果名称是函数名,则为函数调用。另一种情况是所谓的“最烦人的解析”,即不可能区分变量定义和函数声明(有一条规则说它是函数声明)。

That difficulty has introduced the need of typename and template with dependent names. The rest of C++ isn't context sensitive as far as I know (i.e. it is possible to write a context free grammar for it).

这一困难引入了typename和带有依赖名称的模板的需求。就我所知,c++的其余部分对上下文不敏感(也就是说,可以为它编写一个上下文无关的语法)。

#18


1  

Meta-S" is a context-sensitive parsing engine by Quinn Tyler Jackson. I've not used it, but he tells an impressive story. Check out his comments in comp.compilers, and see rnaparse.com/MetaS%20defined.htm – Ira Baxter Jul 25 at 10:42

Meta-S是Quinn Tyler Jackson设计的上下文敏感的解析引擎。我还没用过,但他讲了一个令人印象深刻的故事。查看他在编译器上的评论,见rnaparse.com/metas%20defin. htm - Ira Baxter Jul 25 at 10:42

The correct link is parsing enigines

正确的链接是解析谜

Meta-S was the property of a defunct company called Thothic. I can send a free copy of the Meta-S to anyone interested and I've used it in rna parsing research. Please note the "pseudoknot grammar" included in the examples folders was written by an non-bioinformatics, amature programmer and basically doesn't work. My grammars take a different approach and work quite well.

Meta-S是一家已经倒闭的公司的财产,该公司名为thothought。我可以向任何感兴趣的人发送免费的元数据拷贝,我已经在rna解析研究中使用过它。请注意,示例文件夹中包含的“伪结语法”是由非生物信息学、amature程序员编写的,基本不起作用。我的语法采用了一种不同的方法,并且工作得很好。

#19


-4  

This answer says C++ is not context-free... there's an implication (not by the answerer) that it can't be parsed, and the answer offers a difficult code example which produces an invalid C++ program if a certain constant is not a prime number.

这个回答说c++不是上下文无关的……这里有一个含义(不是回答者),它不能被解析,答案提供了一个复杂的代码示例,如果某个常数不是质数,它将生成一个无效的c++程序。

As others have observed, the question about whether the language is context-sensitive/free is different than the same question about a specific grammar.

正如其他人所观察到的,关于语言是否是上下文敏感/*的问题与关于特定语法的相同问题是不同的。

To set the question about parseability to rest, I offer empirical evidence that there are context-free grammars for C++, that can be used to produce an AST for a context-free parse of the source text by in fact parsing it with an existing GLR-parser-based tool that is driven by an explicit grammar.

parseability设置问题,我提供经验证据是c++的上下文无关文法,可以用来产生一个AST源文本的上下文无关的解析与现有GLR-parser-based实际上解析工具,是由一个显式的语法。

Yes, it succeeds by "accepting too much"; not everything it accepts is a valid C++ program, which is why it's followed up with additional checks (type checks). And yes, the type checker may run into computability problems. In practice tools don't have this problem; if people wrote programs like that, none of them would compile. (I think the standard actually puts a limit on the amount of computation you can do unfolding a template, so in fact the computation is actually finite but probably pretty big).

是的,它的成功在于“接受太多”;它接受的并不是所有东西都是有效的c++程序,这就是为什么它要进行额外的检查(类型检查)。是的,类型检查器可能会遇到可计算性问题。实际上工具没有这个问题;如果人们写这样的程序,没有人会编译。(我认为标准实际上限制了展开模板的计算量,所以实际上计算是有限的,但可能相当大)。

If what you mean is, determine if the source program is a member of the set of valid C++ source programs, then I will agree the problem is a lot harder. But it isn't parsing that is the problem.

如果您的意思是,确定源程序是否为有效c++源程序集的成员,那么我将同意这个问题要困难得多。但问题不在于解析。

The tool resolves this issue by isolating parsing from type-checking the parsed program. (Where there are multiple interpretations in the absence of context, it records an ambiguity node in the parse tree with several possible parses; the type checking decides which one is correct and eliminates the invalid subtrees). You can see a (partial) parse tree in the example below; the whole tree is too large to fit in an SO answer. Note you get a parse tree whether the value 234797 or 234799 is used.

该工具通过将解析与已解析程序的类型检查隔离开来来解决这个问题。(如果在没有上下文的情况下有多种解释,那么它将使用几种可能的解析来记录解析树中的歧义节点;类型检查决定哪个是正确的,并消除无效的子树)。您可以在下面的示例中看到(部分)解析树;整棵树太大了,不适合这么回答。注意,您将得到一个解析树,它是否使用了值234797或234799。

Running the tool's name/type resolver over the AST with the original value 234799 succeeds. With the value 234797 the name resolver fails (as expected) with the error message, "typen is not a type." and thus that version is not a valid C++ program.

使用原始值234799在AST上运行工具的名称/类型解析器将成功。对于值234797,名称解析器(如预期的那样)在错误消息中失败,“类型不是类型”,因此该版本不是有效的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