@@ -947,6 +947,35 @@ _PyPegen_check_legacy_stmt(Parser *p, expr_ty name) {
947947 return 0 ;
948948}
949949
950+ void *
951+ _PyPegen_raise_error_for_missing_comma (Parser * p , expr_ty a , expr_ty b )
952+ {
953+ // Don't raise for legacy statements like "print x" or "exec x"
954+ if (_PyPegen_check_legacy_stmt (p , a )) {
955+ return NULL ;
956+ }
957+ // Only raise inside parentheses/brackets (level > 0)
958+ if (p -> tokens [p -> mark - 1 ]-> level == 0 ) {
959+ return NULL ;
960+ }
961+ // For multi-line expressions (like string concatenations), point to the
962+ // last line instead of the first for a more helpful error message.
963+ // Use a->col_offset as the starting column since all strings in the
964+ // concatenation typically share the same indentation.
965+ if (a -> end_lineno > a -> lineno ) {
966+ return RAISE_ERROR_KNOWN_LOCATION (
967+ p , PyExc_SyntaxError , a -> end_lineno , a -> col_offset ,
968+ a -> end_lineno , a -> end_col_offset ,
969+ "invalid syntax. Perhaps you forgot a comma?"
970+ );
971+ }
972+ return RAISE_ERROR_KNOWN_LOCATION (
973+ p , PyExc_SyntaxError , a -> lineno , a -> col_offset ,
974+ b -> end_lineno , b -> end_col_offset ,
975+ "invalid syntax. Perhaps you forgot a comma?"
976+ );
977+ }
978+
950979static ResultTokenWithMetadata *
951980result_token_with_metadata (Parser * p , void * result , PyObject * metadata )
952981{
0 commit comments