@@ -753,14 +753,92 @@ static OperatorNode parseGoto(Parser parser, int currentIndex) {
753753 }
754754
755755 static OperatorNode parseLocal (Parser parser , LexerToken token , int currentIndex ) {
756+ // Check if this is a declared reference (local \$x, local \@array, etc.)
757+ boolean isDeclaredReference = false ;
758+ if (peek (parser ).type == LexerTokenType .OPERATOR && peek (parser ).text .equals ("\\ " )) {
759+ isDeclaredReference = true ;
760+
761+ // Check if declared_refs feature is enabled
762+ if (!parser .ctx .symbolTable .isFeatureCategoryEnabled ("declared_refs" )) {
763+ throw new PerlCompilerException (
764+ currentIndex ,
765+ "The experimental declared_refs feature is not enabled" ,
766+ parser .ctx .errorUtil
767+ );
768+ }
769+
770+ // Emit experimental warning if warnings are enabled
771+ if (parser .ctx .symbolTable .isWarningCategoryEnabled ("experimental::declared_refs" )) {
772+ // Use WarnDie.warn to respect $SIG{__WARN__} handler
773+ try {
774+ WarnDie .warn (
775+ new RuntimeScalar ("Declaring references is experimental" ),
776+ new RuntimeScalar (parser .ctx .errorUtil .errorMessage (currentIndex , "" ))
777+ );
778+ } catch (Exception e ) {
779+ // If warning system isn't initialized yet, fall back to System.err
780+ System .err .println (parser .ctx .errorUtil .errorMessage (currentIndex , "Declaring references is experimental" ));
781+ }
782+ }
783+
784+ TokenUtils .consume (parser , LexerTokenType .OPERATOR , "\\ " );
785+ }
786+
756787 Node operand ;
757788 // Handle 'local' keyword as a unary operator with an operand
758789 if (peek (parser ).text .equals ("(" )) {
759790 operand = ParsePrimary .parsePrimary (parser );
760791 } else {
761792 operand = parser .parseExpression (parser .getPrecedence ("++" ));
762793 }
763- return new OperatorNode (token .text , operand , currentIndex );
794+
795+ // Check for declared references inside parentheses: local(\$x)
796+ if (operand instanceof ListNode listNode ) {
797+ for (Node element : listNode .elements ) {
798+ if (element instanceof OperatorNode operandNode ) {
799+ // Check if this element is a reference operator (backslash)
800+ // This handles cases like local(\$x) where the backslash is inside the parentheses
801+ if (operandNode .operator .equals ("\\ " ) && operandNode .operand instanceof OperatorNode ) {
802+ // This is a declared reference inside parentheses: local(\$x), local(\@arr), local(\%hash)
803+
804+ // Check if declared_refs feature is enabled
805+ if (!parser .ctx .symbolTable .isFeatureCategoryEnabled ("declared_refs" )) {
806+ throw new PerlCompilerException (
807+ operandNode .tokenIndex ,
808+ "The experimental declared_refs feature is not enabled" ,
809+ parser .ctx .errorUtil
810+ );
811+ }
812+
813+ // Emit experimental warning if warnings are enabled
814+ if (parser .ctx .symbolTable .isWarningCategoryEnabled ("experimental::declared_refs" )) {
815+ // Use WarnDie.warn to respect $SIG{__WARN__} handler
816+ try {
817+ WarnDie .warn (
818+ new RuntimeScalar ("Declaring references is experimental" ),
819+ new RuntimeScalar (parser .ctx .errorUtil .errorMessage (operandNode .tokenIndex , "" ))
820+ );
821+ } catch (Exception e ) {
822+ // If warning system isn't initialized yet, fall back to System.err
823+ System .err .println (parser .ctx .errorUtil .errorMessage (operandNode .tokenIndex , "Declaring references is experimental" ));
824+ }
825+ }
826+
827+ // Mark the nodes as declared references
828+ operandNode .setAnnotation ("isDeclaredReference" , true );
829+ if (operandNode .operand instanceof OperatorNode varNode ) {
830+ varNode .setAnnotation ("isDeclaredReference" , true );
831+ }
832+ }
833+ }
834+ }
835+ }
836+
837+ OperatorNode localNode = new OperatorNode (token .text , operand , currentIndex );
838+ if (isDeclaredReference ) {
839+ localNode .setAnnotation ("isDeclaredReference" , true );
840+ }
841+ return localNode ;
764842 }
765843
766844 static OperatorNode parseReverse (Parser parser , LexerToken token , int currentIndex ) {
0 commit comments