C is completely easy to parse, because it reduces to a LALR(1) parseable context-free language after the semantic mapping of input symbols into abstract lexical categories. In fact, it can be parsed by recursive descent with a suitable factoring, so that it is almost certainly LL(k), possibly with k = 1. Pardon me, k == 1. :)<p>That mapping requires semantic information (type declarations), but that information is something the compiler must maintain anyway so that it can type check the language.<p>Yes, if we simply treat &quot;foo&quot; as an IDENTIFIER token and feed that to the parser, thing swill be difficult. But we don&#x27;t; what we return to the parser is a TYPESPEC token, based on peering into the current parse-time lexical environment where we resolve &quot;foo&quot; to a typedef name. The parser then quite easily goes through the state transitions corresponding to its declaration rule.<p>Sure, a parser which maintains parse-time lexical environments which are also visible to the lexer is slightly harder to write than one which doesn&#x27;t have such stuff (and just returns an unadorned AST which you can walk again to build that info).<p>But if the year is 1973, you would likely do that anyway even if the language doesn&#x27;t require it. Why? Because building the compile-time environment tree reduces the number of passes: from a single pass through the raw code, you already have the syntax tree <i>with</i> type info (and possibly type info that has even been checked).