Is C++ context-free or context-sensitive?
Asked Answered
S

20

443

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

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.

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.)

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

Sclerenchyma answered 29/1, 2013 at 18:5 Comment(30)
Your example clearly shows it to be context sensitive.Suckow
@CarlNorum Please show me a single grammar rule of C++ that does not consist of a single non-terminal symbol on its left-hand side and I will immediately believe you.Sclerenchyma
IIUC it depends a bit on where you draw the line for context-sensitivity. I think I've seen people argue that almost all statically typed programming languages are context-sensitive, not because you can't build a practical compiler for them with CFG parsing tools, but because such implementations "cheat" by parsing some invalid programs and only rejecting them later, during type checking. So if you consider ill-typed programs to be not in the language (in the CS sense, i.e. a set of strings) the parser should accept, more languages than C++ are context-sensitive.Isonomy
Those people are wrong. Formal language theory makes a fine distinction between the stages, and context-sensitive refers specifically to parsing only, no semantics involved.Papua
@pst I honestly thought that "context-sensitive language" was well-defined. Maybe I'm wrong and there are several established definitions? If that's the case, I will happily accept the first answer to explain that to me :)Sclerenchyma
@DeadMG: No, you are wrong. There is no "parsing" or "semantics" in formal language theory at all, just "language" which is a set of strings.Widower
No answers so far have actually addressed your definition of "context-free grammar". To my mind, the correct answer to this question either cites a production in appendix A that does not fit your definition, or demonstrates that your definition is incorrect or insufficient. Stand your ground!Skillern
@FredOverflow Not all "definitions" are Formal, which is why I voted to re-open: the alternatives were winning! In questions like this, it really helps to emphasis/reference the particular point to guide [initial] responses.Rollins
See Is D's grammar really context-free?. In fact, I think everybody here should read that question and its answers!Skillern
Does appendix A claim that it is both a required and sufficient definition of the complete C++ language? Then what is the rest of the standard document (minus the part about the standard library) about?Felipe
@KonradRudolph, I just put the key quote from Appendix A in my answer, below. Appendix A does not claim to be a definition of the language.Bouncer
Looks like none of the answers (including mine) actually answer the question! (For starters, I don't see how a context-sensitive grammar could solve any of these problems people have mentioned without hacks.)Balladeer
@mehrdad, you might not have seen this quote in my response somewhere to someone else: "Computationally, a context-sensitive language is equivalent with a linear bounded nondeterministic Turing machine, also called a linear bounded automaton". I'm not sure what else you expect in an answer.Bouncer
@Bouncer see my other comment below your answer.Balladeer
@mehrdad: that's because the actual solution would require about a planet's worth of productions. The fact that it's turing complete demonstrates that it is possible to write. I don't think you require more for an answer.Bouncer
@Bouncer No one asked for a solution for C++, any context sensitive language that exhibits the same problem/solution would sufficeBalladeer
@mehrdad: math.stackexchange.com/questions/163830/…Bouncer
However, I don't see where OP asked for this. You did. Care to ask a different question?Bouncer
@rici: I might end up doing that, but I feel like any answer that claims C++ is context-sensitive must be prepared to explain how a context-sensitive grammar would be able to parse it.Balladeer
FredOverflow: Okay now I see, @rici's answer is what you're looking for! Make sure to follow the link! It describes how a CSG can look at copies of symbols, which is what we need for C++.Balladeer
If, as you state, "very context-free language is also context-sensitive" and the only two options (per the question) is "C++ context-free or context-sensitive?" then the answer is clearly that it is at least context-sensitive. If, however, you mean your question to be answered as "yes/no", then maybe :-pVedetta
The linked exact duplicate question is not as well constructed as this one. Disagree with close.Drogue
I think the linked question is fine, but the top answers are incorrect. They should probably be merged.Brokendown
@Dervin Thunk Could you kill the acceptance on my answer. I'm convinced that DeamMG et al. are right, but I can't delete this.Grossularite
The problem with this title is that it assumes C++ is at least context-sensitive; it isn't. I'd change it if it wouldn't invalidate so much in the responses.Medalist
@WarrenP: That doesn't mean this isn't a duplicate. However I'd agree with a compromise to close that question as a duplicate of this one, for what will become "historical reasons".Skillern
See also: How Clang handles the type / variable name ambiguity of C/C++ eli.thegreenplace.net/2012/07/05/…Herpes
If question A is good and question B is bad, don't close question A. Close the other one as a duplicate, and leave the good one. The other question had no useful information in its answers. This one has great stuff.Drogue
I wonder I didn't come to this question... - I often hear claims that C++ is a context-sensitive language -- Yes, set of all possible correct programs as a formal language is CFL infact 'C'-language is a CFL -- constraint like declaration first then use later make it CSL - You can't find any single rule as CSG because Compiler is written using the problem is we don't know any efficient parsing technique for CSL, So we use CFG and handles features explicitly programmatically.Ally
Here in my answer: Can someone give a simple but non-toy example of a context-sensitive grammar? I tried to explain why a language is CSL even with same syntax. Applicability of correct syntax-rule on type is also a CS-feature that is in your example. But compile resolve it and pick grammar rule with the help of information stored in symbol-table explicitly (there is no grammar you will find in Appendix).Ally
B
391

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.

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

If you allow arbitrary symbol sequences on both sides of any production, you produce a 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.

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.

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.)

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 .

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.

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.)

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):

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.

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.

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:

α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.

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 α")

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".

Bouncer answered 29/1, 2013 at 18:18 Comment(55)
So not only is it context-sensitive, but it can be made to depend on any context you can express in templates, which are Turing-complete.Papua
@DeadMG: "Computationally, a context-sensitive language is equivalent with a linear bounded nondeterministic Turing machine, also called a linear bounded automaton" (en.wikipedia.org/wiki/Context-sensitive_language) Let me see if I can find an online primary source.Bouncer
How does having multiple symbols on the left hand side of a production solve this problem? I don't think this answer is answering the question.Balladeer
@mehrdad: the ability to put multiple symbols on the left hand side of a production makes the grammar turing-complete. Since parsing the language is turing computable, a context-sensitive grammar could parse it. Actually writing such a CSG would be impractical.Bouncer
@Bouncer you dodged the question :P the current problem with C++ is that it's CFG is ambiguous. My question was, how does a CSG solve that problem example please?)... You responded by telling me it's Turing complete, which completely avoided giving a solution to the problem!Balladeer
@Mehrdad Are you asking how a CSG would solve the problem of the grammar being ambiguous?Isonomy
@mehrdad, the OP says "context-sensitive language", not context-sensitive grammar. Ambiguity is a feature of a grammar, not a language. The language is indeed context-sensitive, but not because a particular grammar for it is ambiguous.Bouncer
Note that my example is not ambiguous. It's an unambiguous expression of a valid program. If you change the value in line 21, it may become ill-formed. But in neither case is it ambiguous.Bouncer
@rici: Yeah, but how would any CSG be able to parse C++ correctly, is the question? I can see the same ambiguity problems with C++ CSGs as I see in C++ CFGs, so I'm failing to see what makes C++ describable by a context-sensitive grammar but not by a context-free grammar.Balladeer
@Mehrdad: I tried to answer that. I fear that it is now an essay on its way to being an introductory text on parsing theory. Prove me wrong by reading it all. (What CSGs are you referring to?)Bouncer
@rici: I read your answer, and it seems to be consistently dodging the question. The question the OP posed is: What makes C++ context-sensitive? The answer I think he (and others) expect to see is: <some particular problem> makes it context-sensitive, and here is how a similar problem, in a simple toy grammar, would be solved by <some sample CSG>, but is impossible to solve with a pure CFG. I have yet to see any answer that actually answers the question of what makes C++ CSL (i.e. describable by a CSG but not a CFG).Balladeer
I have one doubt: As you show, the result of template evaluation can make the difference between a well-formed and an ill-formed program. Template evaluation is turing-complete. So wouldn't correctly determining whether a string is in the language (C++) require turing-completeness? As you say, a context-sensitive language is "just" a "linear bounded automaton", which is not turing-complete AFAIK. Or is your argument making use of the limits the C++ standard puts on some things including template evaluation depth?Isonomy
@delnan, I think I was wrong. I changed the answer to talk about unrestricted grammars.Bouncer
@mehrdad: the copy language is precisely that sort of toy language. Do you expect a formal proof that it's not CFG? Such a proof is in every introductory text that I've seen; it's the classic first use of the pumping lemma.Bouncer
@rici: Ahhh yes, now I finally see it... the answer holds the light! +1 for you, this is an awesome answer then.Balladeer
As far as I know, prime can be decided by a polynomial bounded Turing machine. Hence, the language of all prime numbers should be context-sensitive.Hendley
You write "I think the prime computation can be performed by a linear-bounded Turing machine". I just wrote that it can be done.Hendley
Sorry - could you make the example clearer? I can see why the program fails to compile but my feeling is that it would fail at the semantic analiser rather then parsing - i.e. it have valid syntax but is non a valid program nonetheless (similar problem to variable scope etc.).Tribune
This discussion is growing too long and hard to follow. However it contains good information which should be integrated into the the question or an answer. Please do that and if needed, continue the discussion in the chat!Schreiner
@markus-tharkun, I've been doing that as the discussion progresses, so hopefully there is no useful unintegrated information. If you think I've missed something, let me know or edit it in.Bouncer
@Bouncer no, I don't think you missed anything. I just posted my standard comment for such occasions ;)Schreiner
I'd like to add to your comment about the standard not trying to define an "unrestricted grammar" for C++. It's not just impracticality, but also the fact that it would still not be enough for compiling. That grammar would only recognize a C++ program. Recognizing whether a program is valid or invalid C++ is of little use. You'd also need the parse tree "built" in a well-defined way in order to preserve all the semantics. As noted previously, as long as you can distinguish between a valid and invalid parse, you don't need to care about "ambiguity" at all from a theoretical perspective.Brazil
@MehrdadAfshari, the problem is that general rewrite systems are not a very good way of writing parsers, just like Turing's state machines are not a very good way of writing algorithms. Nonetheless, I can't quite say that a rewrite recognizer would be useless. Augmenting the existing CFG with a some other formal system which could gate particular productions would actually produce a usable parser: the existing CFG provides the grammatical structure. Unfortunately, implementing template instantiation with a rewrite system would be somewhere between a good challenge and BF.Bouncer
@Bouncer I tried not to use the word "useless". My specific point is that a grammar that could accept/reject C++ is not enough to describe the behavior of the output program, just like an ambiguous context-free grammar might not be enough for the same thing (which seems to have been a source of confusion to some people throughout this page confusing it with context-sensitivity of the language defined by the grammar). I agree that it is an interesting challenge. I'd be curious how difficult it can be (even for simpler languages and not C++).Brazil
This answer contains numerous flaws. The word "context" is waved around too informally, which is wrong because the question is about "context-free", which is a formal term. The error in the template is not in fact a syntax error but a semantic error. A language spec can declare "by fiat" that a program which violates some semantic constraint is not well formed and should be diagnosed as if it has a syntax error. But that is just a case of pointing to a tail and calling it a leg. Fact is that the erroneous construct has a parse. That exact piece of text will parse under some conditions.Surefooted
To follow up to Kaz's remarks: the answer should clearly make a distinction between the task of parsing C++ with the objective of interpreting it, and merely recognizing whether a particular piece of code is syntactically valid C++. It should also state what exactly constitutes a definition of when a C++ program is 'syntactically valid'.Medalist
I don't see how template evaluation is the same as parsing, which is what the OP was asking about. I just read this answer as, "A subset of C++ is evaluated at compile time", which happens to happen after parsing but before the 'real' program really runs, so maybe that's why they're being confused here.Archdiocese
@kaz, I made the definition of "context" in "context-sensitive" more precise. (I knew what I meant :), but I agree it wasn't sufficiently obvious.) There is no error in the template; the error (in those cases where there is one) is attempting to invoke a template which doesn't exist. Because of the way C++ parses <, >, and >> (sometimes as operators and sometimes as brackets), it is necessary to know whether a symbol which precedes a < is a template before it is possible to know what lexeme follows it. Once > is known to be an operator, value > () is a syntax error.Bouncer
@JosephGarvin, see the immediately preceding comment. You cannot finish the parse without instantiating the template, so the instantiation cannot happen "after the parse".Bouncer
@SamuelEdwinWard -std=c++11 (or -std=c++0x if you have an older g++)Bouncer
@rici: Perhaps the example would be clearer if both branches compiled, but produced clearly different parse trees.Ringdove
@AntonGolov: My original version of that example did just that (you can achieve it by putting 0 inside of (), for a simple one), but I think it's more interesting this way, because it demonstrates that you need the template instantiation even to recognize if a string is a syntactically correct C++ program. If both branches compile, then I'd have to work harder to contest the argument that the difference is "semantic". Curiously, although I'm often challenged to define "syntactic", no-one has ever offered a definition of "semantic" other than "stuff I don't think is syntactic" :)Bouncer
@rici: If that were true why do you need to use the template keyword when calling a method in a templated base class? I thought that was added to specifically prevent what you're describing. Maybe how we're defining parsing is the issue -- I would consider that as soon as the compiler knows that that typen<> must be a template that the file is 'parsed'. That you get an error when not inputting a prime number I wouldn't describe as a parsing error (even though it occurs before code generation) just like I wouldn't consider a static_assert failure a parsing error.Archdiocese
@JosephGarvin: you need the template keyword only when the identifier which names the template is a dependent name. That doesn't apply in the case of my program. If the identifier is not a dependent name, then the template keyword is, ahem, a syntax error. (This surprised me, but it's true. Try it.) By the way, personally I don't think the file is parsed until the last character is parsed, but your definition works: The compiler doesn't know that typen is a template until after it instantiates foo<IsPrime<234799>> (since it wouldn't be with foo<IsPrime<234797>>).Bouncer
@JosephGarvin: small correction. Apparently, you can use template even if the compiler can figure it out for itself. (As long as the name is qualified.) So, indeed, I could have written template typen, which would have caused the syntax error to be noticed a couple of tokens earlier, since it would then be an immediate error after the non-prime foo<IsPrime<234797>> template was instantiated, instead of waiting until the > is parsed as an comparison operator and the () is found to not be parseable as an expression.Bouncer
@rici: I see, but I still find the terminology somewhat misleading. Calling it a syntax error suggests that passing a composite number to the IsPrime template is a syntax mistake, when only a language lawyer would identify it as such. To be really pedantic it seems like a higher order type error, where passing a composite number doesn't higher-type-check because typen is not a type of type that takes template arguments.Archdiocese
@josephGarvin: passing a composite number to the IsPrime template is just fine. It's not a type error at all; it's a simple syntax error. You simply can't use () as an expression, that's all. (Of course, the real problem is that in this particular case, it's not easy to predict that < is an operator; in particular, human beings get it wrong because we do actually (and sometimes incorrectly) use whitespace-sensitive parsing. But the compiler knows the syntax better.) Anyway, I wasn't talking about language legality; I was talking about formal grammar theory.Bouncer
@rici: Ah, now I get it. When you pass a composite the error technically comes from the () not being an expression even though the compiler only goes for that interpretation based on typen not being a template, rather than erroring due to typen not being a template.Archdiocese
The IsPrime<...> example (complete with program) was extreme overkill for such a simple point... A more straightforward (as in less convoluted) example would have been something like: nullptr_t n = std::conditional<true, nullptr_t, int>::type{}; which is "syntactically correct" (as the OP phrased it) if and only if the first template parameter is true.Ballad
@etherice: I agree with your usage of "syntactically correct" (which was my phrase, not OP's) but many commentators here don't; I think they would describe your example as being syntactically correct but semantically incorrect (since it is a type error). IsPrime came out of an investigation of how to syntax-colour C++; it was part of my realization that it's really not practical to definitively decide whether < is an operator or a bracket. (Fortunately, there are good heuristics, and syntax-colouring is allowed to be wrong in pathological cases.)Bouncer
@rici: I actually side with the other commenters that the error is a semantic one, not a syntactical one, but I admit it's hard to classify a template-meta-programming error. The point of my comment was to show that your IsPrime example was a bit "esoteric" for the relatively simple point you were trying to make. Ultimately, whether or not your program compiled depended on 1) the result of template-meta-programming, and 2) whether or not the resultant rvalue type is implicitly-convertible to the lvalue type. That seems like a semantic issue, not a syntactic one.Ballad
@etherice: The only lvalue in my little program is auto so there is no type conversion at all; I think you're misunderstanding the nature of the program. The syntactic question is whether typen<1>() is (a) a template instantiation or (b) a simple expression. In other words, whether the < and > are template brackets or comparison operators. In the former case, () is an empty parameter list; in the latter case, it's a syntax error. I don't see how you can call this syntax error anything other than a syntax error, and that was the point of the example.Bouncer
@rici: There is type deduction, which is still a semantic not syntactic issue. I took another look at the program, and it can be simplified to auto b = std::enable_if< true/false , SomeType >(); ... whether or not it compiles depends on the C++ principle of SFINAE, which is definitely a semantic matter. I see what you're saying about parsing the RHS expression as (foo<false>::typen < 1) > () as a "most vexing parse" scenario that results in a syntax error at () as the 2nd argument to operator>. It's interesting enough to make its own SO question for the C++ experts to disect.Ballad
@etherice: I think you need to look at it some more :). It's not the "most vexing parse" since it is clearly an assignment statement. Also note that if you put a value inside the parentheses, say typen<1>(42)', then it would not be a syntax error regardless of whether IsPrime` is instantiated with a prime or not, but the two cases produce radically different parses. What you're maybe missing is that typen might be a template alias, or it might be a static const int; you need to know which one it is in order to parse what follows typen. By parse, I mean parse. Hence syntax.Bouncer
@rici: I took another (final) look, this time actually using a compiler, and the program simplifies to these 3 statements: struct foo { typedef int type; }; struct bar { const int type = 0; }; and either auto b = foo::type(); or auto b = bar::type(); ... The foo case is fine as it creates a default int. The bar case becomes auto b = 0(); which is, in fact, a syntax error in this simplified form. However, in its template-form, it "seems" more like a semantic issue since it depends on the results of a template-meta-program (albeit a trivial one), which must be evaluated.Ballad
@rici: The <false> case more specifically comes down to auto b = 0<1>(); which goes back to my previous point that it could be parsed as auto b = ((0 < 1) > ()); in which case the syntax error occurs at the 2nd argument to operator>, making the entire expression invalid. The reason typen<1>(42) works is that it's the same as X<1>(42) which implicitly constructs an X from int value 42. If the constructor were declared explicit, that would not be possible (once again, semantics).Ballad
@etherice: The reason typen<1> works is that typen is a template. That's independent of the explicitness of the constructor. If typen were a non-template type, then typen<1> would be a different syntax error, because a typename cannot be an operand to the operator <. By the way, there is no optionality about the parse. It must be parsed either as a templated constructor or as an expression, depending on the "kind" of the member symbol typen; it is not a resolved ambiguity, unlike the "vexing parse". But enough.Bouncer
@rici: Yes, I know typen is a template<int> struct X in the <true> case, and therefore typen<1> is X<1>, and X<1>(42) implicitly constructs a X<1>. Where you're wrong is in the <false> case, where typen is a const int, which can obviously be the operand to an operator< invocation, making it possible to parse it as the 1st argument to a operator< for int arguments. I agree with the "enough" part, as we seem to have a disconnect here. For example, I said typen<1>(42) and you read it as typen<1>, etc.Ballad
let us continue this discussion in chatBouncer
"Writing a Turing machine to parse C++ would be an equally impossible undertaking." If any machine can ever exist to parse C++, which I assume does exist, a Turing machine to do so exists too. Just saying. Claiming a parser for C++ is Turing complete and saying it's impossible to write a Turing machine for it is a contradiction.Corticosteroid
@shellfish: That Turing machine exists in the same way that the decimal representation of the fifth Ackermann number (oeis.org/A189896) exists. I do not believe it is at all contradictory to assert to no human being will ever write that number out. It is impossible, within the physical constraints of the universe. That's the impossibility I was referring to. Probably, the Turing machine is not quite as big as a(5), but I don't feel like I go too far out on a limb asserting that the task of writing out it's state transition table is not a very plausible project.Bouncer
@Bouncer Oh okay, I interpreted that differently. Let's call it semantics. Amazing post by the way, definitely the best discussion I've ever read on SO!Corticosteroid
@Bouncer Here is a slightly cleaner C++14 version of your (excellent) example: melpon.org/wandbox/permlink/8tpay0SxHvWXfDnyBreezeway
I'd also like to point out that C++14 generalized constexpr can be used to create a similar example with much less mystique (no need for recursive inheritance).Breezeway
I don’t understand the point of the example with primes. Primality is decidable in NSPACE(O(n)), and therefore by a context-sensitive grammar.Blackjack
W
127

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.

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

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.

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

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.

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).

Widower answered 29/1, 2013 at 18:43 Comment(19)
ambiguity doesn't have anything to do with context-sensitivity This was my intuition too, so I'm glad to see someone (a) agree, and (b) explain it where I could not. I believe it disqualifies all the arguments that are based on a b(c);, and partially satisfy the original question whose premise was "oft-heard" claims of context-sensitivity being due to ambiguity... especially when for the grammar there's actually no ambiguity even in the MVP.Skillern
@Non-StopTimeTravel: Yeah, that's another reason why I deleted my answer, after I thought about it some more. I think an ambiguous grammar is completely fine as long as the strings it produces are those (and only those) inside the language. Which is why I'm failing to see how a CSG would succeed at any problems that a CFG has failed in...Balladeer
This would be a good answer were it not for the following sentence: “ 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.” – This is wrong, Context sensitive is equivalent to Turing complete.Felipe
@KonradRudolph: Can you provide evidence of that? (though I'm not disputing it)Skillern
@Non-StopTimeTravel Is the Wikipedia article not enough for you? It’s textbook knowledge.Felipe
@KonradRudolph I'd like a reference too. en.wikipedia.org/wiki/Context-sensitive_language says that context sensitive = Linear bounded automaton, which (from my limited understanding) is not turing-complete because of the limited tape length.Isonomy
@delnan So do C++ templates. The standard defines a maximum depth that a conforming compiler has to provide (at minimum).Felipe
@KonradRudolph: An assertion without any reference is not enough for me on SO, no. If you're citing a passage on Wikipedia, please provide a link and a quote.Skillern
@KonradRudolph Okay, I know of this limitation but never see anyone base their argument on it, so I generally don't give people the benefit of doubt ;-)Isonomy
@Non-StopTimeTravel No, sorry. I do not provide sources for “general reference” knowledge that can be trivially found on Google or Wikipedia without having to dig for them.Felipe
@KonradRudolph: What the standard says is "There is an implementation-defined quantity that specifies the limit on the total depth of recursive instantiations, which could involve more than one template. The result of an infinite recursion in instantiation is undefined." (14.7.1p15) I interpret that to mean that an implementation is not required to understand every valid c++ program, not that programs with too large a recursion depth are invalid. The only ones which are marked as invalid are those with an infinite recursion depth.Bouncer
@Bouncer Ah. Yes, I see. I think previous versions of the standard explicitly specified an upper level (which was pretty low, and the cause of much ire, if I remember correctly).Felipe
@Konrad: That's not very scientific of you. I'm really good at Googling and didn't find anything "trivially". Since you have been unwilling or unable to provide a reference when asked, I have no choice but to declare your assertion as inadmissable!Skillern
@Non-StopTimeTravel Nonsense. In fact, it’s common in scientific discussions to take “general reference” knowledge for granted. And I have even pointed you to Wikipedia. Do you really want me to believe that you have read the short article about “context-sensitive grammar” and not found the relevant paragraph?Felipe
@KonradRudolph: I dispute that it's "general reference". The fact that I read that rather complex article and do not comprehend it sufficiently to piece out this little fact should be enough to demonstrate that. It's not as if you said something like "computers commonly use electricity", or "bits can be true or false".Skillern
If this fact really is so widely known I'd think it would be much easier to find a reference to it than to argue at length about whether or not one should be provided. Not to mention constructive.Discography
As far as I can tell, @Konrad was mistaken when he said "Context sensitive is equivalent to Turing complete." (at least, he was if he was denoting "Recursively enumerable" by "Turing complete"), and has since been unable to recognize this mistake. Here is a reference for the proper set inclusion relationship involved here: en.wikipedia.org/wiki/Chomsky_hierarchyPahl
or people use the term "context-free language" for the opposite, a CFG can describe a superset of the (valid) strings comprising the language (including non-valid ones as per your example). sidenote: i would not label people working with implementations "ignorant", just because they use a specific implementation of CFGs instead of an abstract formalityJenniejennifer
Can we say that the C++ language is defined by the grammar in the specification, with restrictions? Can this be modelled with a minimal grammar, also with restrictions, to illustrate the situation more easily?Drinkable
W
62

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).

#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 );
Wheel answered 23/7, 2009 at 16:46 Comment(3)
Why can that problem not be solved like for C, by remembering which type definitions are in scope?Ludwick
@Blaisorblade: One way to make a compiler "clean" is to separate tasks into independent steps in a chain, such as creating a parse tree from the input followed by a step that does the type analysis. C++ forces you to either 1) merge these steps into one or 2) parse the document according to both/all possible interpretations, and allowing the type resolution stages to narrow it down to the correct interpretation.Wheel
@280Z28: agreed, but that's the case for C, too; I think a good answer to this question should show why C++ is worse than C. The PhD thesis linked here does that: https://mcmap.net/q/81747/-why-can-39-t-c-be-parsed-with-a-lr-1-parserLudwick
H
30

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 grammar.

  2. However, even if a program conforms to the context-free grammar defined by the programming language, it is not necessarily a valid program. There are many non-context-free pperties 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.

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

Hendley answered 29/1, 2013 at 18:52 Comment(3)
It's interesting to note that you often have to place the "mere syntax" level lower than you'd expect, in order to get a CFG for your programming language. Take C, for instance. You might think that the grammar rule for a simple variable declaration in C would be VARDECL : TYPENAME IDENTIFIER, but you cannot have that, because you cannot distinguish type names from other identifiers at a CF level. Another example: at a CF level, you cannot decide whether to parse a*b as a variable declaration (b of type pointer to a) or as a multiplication.Ariew
@LaC: Yes, thanks for pointing this out! By the way, I'm sure that there is a more commonly used technical term for mere syntax. Does anyone the correct term?Hendley
@Dan: what you're talking about is an approximation of the language given by some context-free grammar. Of course such an approximation is coontext-free by definition. This is the sense in which "syntax" is often use when discussing programming languages.Medalist
M
15

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.

Mutant answered 23/7, 2009 at 17:0 Comment(8)
Wow... Thanks. I wonder if it really makes sense to think about using anything more powerful than a CFG to parse any artificial language.Ichneumon
Great book for understanding the whys of C++. I recommend that and Lippman's Inside the C++ Object Model for understanding how C++ works. Though both are a bit dated they are still a good read.Argyres
"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.htmSinaloa
@IraBaxter: your x-ref is MIA today - and solid references to the software seem to be elusive (Google search doesn't provide any good leads, either with 'site:rnaparse.com meta-s' or 'quinn jackson meta-s'; there are bits and pieces, but meta-s.com leads to a non-informative web-site, for example).Etheleneethelin
@Jonathan: been awhile, just noticed your complaint. Dunno why the link is bad, I thought it was good when I wrote it. Quinn used to be pretty active in comp.compilers. Google seems to be getting flaky, this is all I can find: groups.google.com/group/comp.compilers/browse_thread/thread/… IIRC, he signed over rights to MetaS to some outfit in Hawaii to re-market. Given how tecnically odd this was, IMHO this is signing its death warrant. Sounded like a very clever scheme.Sinaloa
@Ira: there's a deleted answer at the bottom which you should be able to read from Quinn Tyler Jackson about Meta-S. It (Meta-S) may well have died by now.Etheleneethelin
@Jonathan: Ah, yes, typical Quinn response. Good luck with it. Someday I should go read the tech report (I think) he wrote about it. I understand how it (might have) died; its pretty hard to get people to take up an arcane technology. Witness what I do as another example :-{Sinaloa
@Jonathan: Actually, there's an undeleted answer in which he refers to the mysterious company name: Thothic. I've added a link to the company web site in the comments. I have zero knowledge of them.Sinaloa
W
13

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).

First example:

A*B;

Is this a multiplication expression?

OR

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

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

Second example:

A B(bar);

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

OR

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

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

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.

Weevil answered 31/10, 2011 at 20:51 Comment(2)
A B(); is a function declaration even in a function definition. Look for most vexing parse...Devorahdevore
"You cannot build the syntax tree by simply parsing through the file " FALSE. See my answer.Sinaloa
B
12

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.

Blucher answered 29/1, 2013 at 18:26 Comment(3)
How does having multiple symbols on the left hand side of a production solve this problem? I don't think this answer is answering the question.Balladeer
My answer is in response to the first sentence: "I often hear claims that C++ is a context-sensitive language." If those claims use the expression "context-sensitive" informally, then there's no problem. I don't think that C++ is formally context-sensitive.Blucher
I think C++ is formally context-sensitive, but the problem I'm having is that I don't understand how a context-sensitive grammar would have any more success at parsing C++ than a CFG would.Balladeer
T
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.

look also,

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


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.

Torsi answered 23/7, 2009 at 16:37 Comment(1)
Most C++ parsers do not use GLR parsing technology. GCC doesn't. Some do. See semanticdesigns.com/Products/FrontEnds/CppFrontEnd.html for one that does.Sinaloa
C
6

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.

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.

Cornhusk answered 30/1, 2013 at 18:58 Comment(0)
M
5

True :)

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

Misprint answered 23/7, 2009 at 16:37 Comment(0)
C
5

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

Capacitate answered 23/7, 2009 at 16:50 Comment(0)
P
5

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.

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).

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.

Papua answered 29/1, 2013 at 18:8 Comment(7)
Also a<b<c>>d, right? (Your example is actually a classic from C, where it is the only obstruction to being context-free.)Strawberry
That's more of a lexing issue, I think. But is certainly in the same category, yes.Papua
The questioner doesn't ask how it's more context-sensitive than C, only to show that it is context-sensitive.Papua
So.. is C++ more context-sensitive than C?Strawberry
Pretty sure there are more ambiguities, yes.Papua
@DeadMG I don't think you're answering the question (I don't think I was either). How does having terminals on the left hand side of a production solve this problem?Balladeer
The question doesn't ask. It only asks if C++ is context sensitive. It is. That's all there is to it. I believe that you'd have to make the additional terminals match the typedef, then you can match it to a function decl, or you make the additional terminals match a variable definition, then you can match it to variable decl. But you'd have to ask someone who cares about context-sensitive grammars except not producing one or hacking around the context-sensitive part, not me.Papua
D
5

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.

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.

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).

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.

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.

Dannie answered 31/1, 2013 at 0:1 Comment(1)
The (context-free) productions define the most vexing parse well enough so it can be parsed by a context free parsing engine. That delays the problem of deciding which of multiple interpretations are valid until after the parse is complete, but that just make the engineering of the parser and name resolver easier, because they are modular rather than tangled as in conventional C++ parsers. See AST for most vexing parse: #17389271Sinaloa
R
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.

Rundgren answered 31/1, 2013 at 8:41 Comment(5)
I believe C++11 mandates the latter interpretation, and you have to add parens to opt-in to the former.Archdiocese
@JosephGarvin, no. C++ mandates that < must be a bracket if it could be (eg., it follows an identifier which names a template). C++11 added the requirement that > and the first character of >> be interpreted as close brackets if that use is plausible. This affects the parsing of a<b>c> where a is a template but has no effect on a<b<c>.Bouncer
@aaron: how is that simpler than a(); (which is either expr.call or expr.type.conv)?Bouncer
@rici: Oops, I didn't realize it was asymmetric.Archdiocese
Are you describing ambiguity, or context sensitivity?Beslobber
B
4

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

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.

{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.}

Brose answered 15/11, 2009 at 20:54 Comment(0)
H
3

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:

Wikipedia Talk: Context-Free grammar

Regards,
Ovanes

Harriot answered 23/7, 2009 at 16:42 Comment(0)
D
2

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:

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).

Devorahdevore answered 23/7, 2009 at 17:2 Comment(0)
R
2

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

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.

Respire answered 18/9, 2009 at 11:31 Comment(1)
This is actually an interesting find.Ichneumon
R
1

A big issue here is that the terms "context-free" and "context-sensitive" are a little unintuitive within computer science. For C++, context-sensitivity looks a lot like ambiguity, but that's not necessarily true in the general case.

In C/++, an if statement is only allowed inside a function body. That would seem to make it context-sensitive, right? Well, no. Context-free grammars don't actually need the property where you can pluck out some line of code and determine whether it's valid. That's not actually what context-free means. It's really just a label that vaguely implies something kind of related to what it sounds like.

Now, if a statement within a function body is parsed differently depending on something defined outside immediate grammatical ancestors (e.g. whether an identifier describes a type or variable), as in the a * b; case, then it is, in fact, context-sensitive. There is no actual ambiguity here; it will be parsed as a declaration of a pointer if a is a type and a multiplication otherwise.

Being context-sensitive does not necessarily mean "hard to parse". C is actually not that hard because the infamous a * b; "ambiguity" can be resolved with a symbol table containing typedefs encountered previously. It doesn't require any arbitrary template instantiations (which have been proven to be Turing Complete) to resolve that case like C++ does on occasion. It's not actually possible to write a C program that will not compile in a finite amount of time even though it has the same context-sensitivity that C++ does.

Python (and other whitespace-sensitive languages) is also context-dependent, as it requires state in the lexer to generate indent and dedent tokens, but that doesn't make it any harder to parse than a typical LL(1) grammar. Python prior to version 3.9 actually used a LL(1) parser generator, which is part of why it has/had such uninformative syntax error messages (Many of which have been fixed since switching to a PEG parser). It's also important to note here that there is no "ambiguity" like the a * b; problem in Python, giving a good concrete example of a context-sensitive language without "ambiguous" grammar (as mentioned in the first paragraph).

Rudyrudyard answered 29/7, 2019 at 16:34 Comment(0)
S
-3

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.

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.

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).

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.

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.

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.

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
Sinaloa answered 29/5, 2016 at 4:37 Comment(3)
Determining if it's a variable declaration or a multiplication is not a type checking feature. Also I had to scrub your answer of that self-promotion stuff... again.Papua
@Puppy: you can say what you like, but that's how the tool works. Deleting the tool name is probably just going to make people ask what the tool name is.Sinaloa
Whether or not that's how the tool works is irrelevant, since the question does not ask for the working of the tool. Also, I think that we can safely wait for that to actually happen.Papua

© 2022 - 2024 — McMap. All rights reserved.