@@ -598,12 +598,54 @@ def _set_up_rule_memoization(self, node: Rule, result_type: str) -> None:
598598 def _should_memoize (self , node : Rule ) -> bool :
599599 return "memo" in node .flags and not node .left_recursive
600600
601+ def _atom_fast_path_rules (self ) -> frozenset :
602+ # Rules marked with the @atom_fastpath meta in the grammar: pure
603+ # entry points into the expression precedence chain, for which a
604+ # bare NAME/NUMBER atom followed by a token that cannot start or
605+ # continue a binary/postfix expression parses to exactly that atom,
606+ # so a two-token fast path is emitted that builds it directly.
607+ meta = self .grammar .metas .get ("atom_fastpath" )
608+ rules = frozenset (meta .split ()) if meta else frozenset ()
609+ for rulename in rules :
610+ if rulename not in self .rules :
611+ raise ValueError (
612+ f"@atom_fastpath names unknown rule { rulename !r} "
613+ )
614+ return rules
615+
616+ def _emit_atom_fast_path (self ) -> None :
617+ # Only look one token ahead when the current token is NAME/NUMBER:
618+ # the tower fills it too in that case, so error reporting (which
619+ # keys off p->fill) is unperturbed.
620+ self .print ("if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) {" )
621+ with self .indent ():
622+ self .print ("p->error_indicator = 1;" )
623+ self .add_return ("NULL" )
624+ self .print ("}" )
625+ self .print ("Token *_fast_tok = p->tokens[p->mark];" )
626+ self .print ("if (_fast_tok->type == NAME || _fast_tok->type == NUMBER) {" )
627+ with self .indent ():
628+ self .print ("if (p->mark + 1 == p->fill && _PyPegen_fill_token(p) < 0) {" )
629+ with self .indent ():
630+ self .print ("p->error_indicator = 1;" )
631+ self .add_return ("NULL" )
632+ self .print ("}" )
633+ self .print ("if (_PyPegen_atom_follow_token(p->tokens[p->mark + 1]->type)) {" )
634+ with self .indent ():
635+ self .print ("expr_ty _fast_res = (_fast_tok->type == NAME)" )
636+ self .print (" ? _PyPegen_name_token(p) : _PyPegen_number_token(p);" )
637+ self .add_return ("_fast_res" )
638+ self .print ("}" )
639+ self .print ("}" )
640+
601641 def _handle_default_rule_body (self , node : Rule , rhs : Rhs , result_type : str ) -> None :
602642 memoize = self ._should_memoize (node )
603643
604644 with self .indent ():
605645 self .add_level ()
606646 self ._check_for_errors ()
647+ if node .name in self ._atom_fast_path_rules ():
648+ self ._emit_atom_fast_path ()
607649 self .print (f"{ result_type } _res = NULL;" )
608650 if memoize :
609651 self .print (f"if (_PyPegen_is_memoized(p, { node .name } _type, &_res)) {{" )
0 commit comments