@@ -2036,20 +2036,35 @@ impl<'a> Parser<'a> {
20362036 // recursive `parse_subexpr` would re-walk the rest of the chain at
20372037 // every dot.
20382038 _ => {
2039- let expr = self.maybe_parse(|parser| {
2040- let expr = parser.parse_prefix()?;
2041- match &expr {
2042- Expr::CompoundFieldAccess { .. }
2043- | Expr::CompoundIdentifier(_)
2044- | Expr::Identifier(_)
2045- | Expr::Value(_)
2046- | Expr::Function(_) => Ok(expr),
2047- _ => parser.expected_ref(
2048- "an identifier or value",
2049- parser.peek_token_ref(),
2050- ),
2051- }
2052- })?;
2039+ // For a plain `Word` field (not followed by `(`), skip the
2040+ // speculative `parse_prefix`. The only result the validator
2041+ // below would accept is `Identifier`, which `parse_identifier`
2042+ // in the None branch produces directly. This avoids 2^N work
2043+ // on chains like `.not-b.not-b...` where `parse_prefix` would
2044+ // descend into `parse_not` and re-walk the remaining chain at
2045+ // every segment.
2046+ let word_field_no_lparen =
2047+ matches!(self.peek_token_ref().token, Token::Word(_))
2048+ && self.peek_nth_token_ref(1).token != Token::LParen;
2049+
2050+ let expr = if word_field_no_lparen {
2051+ None
2052+ } else {
2053+ self.maybe_parse(|parser| {
2054+ let expr = parser.parse_prefix()?;
2055+ match &expr {
2056+ Expr::CompoundFieldAccess { .. }
2057+ | Expr::CompoundIdentifier(_)
2058+ | Expr::Identifier(_)
2059+ | Expr::Value(_)
2060+ | Expr::Function(_) => Ok(expr),
2061+ _ => parser.expected_ref(
2062+ "an identifier or value",
2063+ parser.peek_token_ref(),
2064+ ),
2065+ }
2066+ })?
2067+ };
20532068
20542069 match expr {
20552070 // If we get back a compound field access or identifier,
0 commit comments