11import { Parser } from '../parser' ;
22import { Node , NodeAbstract } from '../node' ;
3+ import { Expr } from '../node/expr' ;
34import { Token } from '../token' ;
45import { Lexer } from '../lexer' ;
56import { PhpParserError } from '../error' ;
@@ -1755,8 +1756,8 @@ export class Php8Parser implements Parser {
17551756
17561757 // ─── Expression parsing (Pratt parser / precedence climbing) ──
17571758
1758- private parseExpressionList ( ) : Node [ ] {
1759- const exprs : Node [ ] = [ ] ;
1759+ private parseExpressionList ( ) : Expr [ ] {
1760+ const exprs : Expr [ ] = [ ] ;
17601761 if ( this . is ( ';' . charCodeAt ( 0 ) ) || this . is ( ')' . charCodeAt ( 0 ) ) ) return exprs ;
17611762 do {
17621763 if ( this . is ( ')' . charCodeAt ( 0 ) ) ) break ; // trailing comma
@@ -1770,22 +1771,22 @@ export class Php8Parser implements Parser {
17701771 // 5: ternary (?:) 6: ?? (coalesce) 7: pipe 8: ||
17711772 // 9: && 10: | 11: ^ 12: & 13: ==/===/!=/!==/<=>
17721773 // 14: </<=/>/>= 15: <</> 16: +/-/. 17: */% 18: instanceof 19: **
1773- private parseExpression ( minPrec : number = 0 ) : Node {
1774+ private parseExpression ( minPrec : number = 0 ) : Expr {
17741775 let left = this . parseUnaryExpression ( ) ;
17751776
17761777 while ( true ) {
17771778 // Check binary operators first
17781779 const op = this . getBinaryOp ( ) ;
17791780 if ( op !== null && op . prec >= minPrec ) {
17801781 this . advance ( ) ;
1781- let right : Node ;
1782+ let right : Expr | Node ;
17821783 if ( op . type === 'Instanceof' ) {
17831784 right = this . parseInstanceofRhs ( ) ;
17841785 } else {
17851786 const nextMinPrec = op . rightAssoc ? op . prec : op . prec + 1 ;
17861787 right = this . parseExpression ( nextMinPrec ) ;
17871788 }
1788- left = this . createBinaryOp ( op . type , left , right , {
1789+ left = this . createBinaryOp ( op . type , left , right as Expr , {
17891790 ...left . getAttributes ( ) ,
17901791 endLine : ( right as any ) . getEndLine ?.( ) ?? - 1 ,
17911792 } ) ;
@@ -1795,7 +1796,7 @@ export class Php8Parser implements Parser {
17951796 // Check ternary (?:) at precedence 5 — left-associative
17961797 if ( this . is ( '?' . charCodeAt ( 0 ) ) && minPrec <= 5 ) {
17971798 this . advance ( ) ;
1798- let ifTrue : Node | null = null ;
1799+ let ifTrue : Expr | null = null ;
17991800 if ( ! this . is ( ':' . charCodeAt ( 0 ) ) ) {
18001801 ifTrue = this . parseExpression ( ) ;
18011802 }
@@ -1889,7 +1890,7 @@ export class Php8Parser implements Parser {
18891890 }
18901891 }
18911892
1892- private static readonly BINARY_OP_MAP : Record < string , new ( left : Node , right : Node , attrs : Record < string , any > ) => Node > = {
1893+ private static readonly BINARY_OP_MAP : Record < string , new ( left : Expr , right : Expr , attrs : Record < string , any > ) => Expr > = {
18931894 'BitwiseAnd' : BinBitwiseAnd ,
18941895 'BitwiseOr' : BinBitwiseOr ,
18951896 'BitwiseXor' : BinBitwiseXor ,
@@ -1933,12 +1934,12 @@ export class Php8Parser implements Parser {
19331934 this . advance ( ) ;
19341935 return new Name ( 'static' , this . endAttributes ( attrs ) ) ;
19351936 }
1936- if ( this . is ( T . T_SELF ) ) {
1937+ if ( this . is ( T . T_STRING ) && this . current ( ) . text === 'self' ) {
19371938 const attrs = this . startAttributes ( ) ;
19381939 this . advance ( ) ;
19391940 return new Name ( 'self' , this . endAttributes ( attrs ) ) ;
19401941 }
1941- if ( this . is ( T . T_PARENT ) ) {
1942+ if ( this . is ( T . T_STRING ) && this . current ( ) . text === 'parent' ) {
19421943 const attrs = this . startAttributes ( ) ;
19431944 this . advance ( ) ;
19441945 return new Name ( 'parent' , this . endAttributes ( attrs ) ) ;
@@ -1947,7 +1948,7 @@ export class Php8Parser implements Parser {
19471948 return this . parseUnaryExpression ( ) ;
19481949 }
19491950
1950- private createBinaryOp ( type : string , left : Node , right : Node , attrs : Record < string , any > ) : Node {
1951+ private createBinaryOp ( type : string , left : Expr , right : Expr , attrs : Record < string , any > ) : Expr {
19511952 if ( type === 'Instanceof' ) {
19521953 return new Instanceof_ ( left , right , attrs ) ;
19531954 }
@@ -1958,7 +1959,7 @@ export class Php8Parser implements Parser {
19581959 throw new Error ( 'Unknown binary op: ' + type ) ;
19591960 }
19601961
1961- private static readonly ASSIGN_OP_MAP : Record < string , new ( var_ : Node , expr : Node , attrs : Record < string , any > ) => Node > = {
1962+ private static readonly ASSIGN_OP_MAP : Record < string , new ( var_ : Expr , expr : Expr , attrs : Record < string , any > ) => Expr > = {
19621963 'BitwiseAnd' : AssignBitwiseAnd ,
19631964 'BitwiseOr' : AssignBitwiseOr ,
19641965 'BitwiseXor' : AssignBitwiseXor ,
@@ -1974,7 +1975,7 @@ export class Php8Parser implements Parser {
19741975 'ShiftRight' : AssignShiftRight ,
19751976 } ;
19761977
1977- private arrayToList ( node : Node ) : Node {
1978+ private arrayToList ( node : Expr ) : Expr {
19781979 if ( node instanceof ExprArray_ ) {
19791980 // Recursively convert nested arrays to lists
19801981 const items = node . items . map ( ( item : any ) => {
@@ -1989,7 +1990,7 @@ export class Php8Parser implements Parser {
19891990 return node ;
19901991 }
19911992
1992- private createAssignOp ( type : string , left : Node , right : Node , attrs : Record < string , any > ) : Node {
1993+ private createAssignOp ( type : string , left : Expr , right : Expr , attrs : Record < string , any > ) : Expr {
19931994 if ( type === 'Assign' ) {
19941995 return new Assign ( this . arrayToList ( left ) , right , attrs ) ;
19951996 }
@@ -2001,7 +2002,7 @@ export class Php8Parser implements Parser {
20012002 return new Assign ( left , right , attrs ) ;
20022003 }
20032004
2004- private parseUnaryExpression ( ) : Node {
2005+ private parseUnaryExpression ( ) : Expr {
20052006 const attrs = this . startAttributes ( ) ;
20062007 const id = this . currentId ( ) ;
20072008
@@ -2156,7 +2157,7 @@ export class Php8Parser implements Parser {
21562157 return this . parsePostfixExpression ( ) ;
21572158 }
21582159
2159- private parsePostfixExpression ( ) : Node {
2160+ private parsePostfixExpression ( ) : Expr {
21602161 let expr = this . parsePrimaryExpression ( ) ;
21612162
21622163 while ( true ) {
@@ -2232,7 +2233,7 @@ export class Php8Parser implements Parser {
22322233 }
22332234 } else if ( this . is ( '[' . charCodeAt ( 0 ) ) ) {
22342235 this . advance ( ) ;
2235- let dim : Node | null = null ;
2236+ let dim : Expr | null = null ;
22362237 if ( ! this . is ( ']' . charCodeAt ( 0 ) ) ) {
22372238 dim = this . parseExpression ( ) ;
22382239 }
@@ -2260,7 +2261,7 @@ export class Php8Parser implements Parser {
22602261 return expr ;
22612262 }
22622263
2263- private parsePrimaryExpression ( ) : Node {
2264+ private parsePrimaryExpression ( ) : Expr {
22642265 const attrs = this . startAttributes ( ) ;
22652266
22662267 switch ( this . currentId ( ) ) {
@@ -2494,7 +2495,7 @@ export class Php8Parser implements Parser {
24942495
24952496 // ─── Helper expression parsers ─────────────────────────────────
24962497
2497- private parseCallableVariable ( ) : Node {
2498+ private parseCallableVariable ( ) : Expr {
24982499 const attrs = this . startAttributes ( ) ;
24992500 if ( this . is ( '$' . charCodeAt ( 0 ) ) ) {
25002501 this . advance ( ) ; // consume $
@@ -2514,7 +2515,7 @@ export class Php8Parser implements Parser {
25142515 return new Variable ( token . text . substring ( 1 ) , this . endAttributes ( attrs ) ) ;
25152516 }
25162517
2517- private parseSimpleVariable ( ) : Node {
2518+ private parseSimpleVariable ( ) : Expr {
25182519 const attrs = this . startAttributes ( ) ;
25192520 const token = this . expect ( T . T_VARIABLE ) ;
25202521 return new Variable ( token . text . substring ( 1 ) , this . endAttributes ( attrs ) ) ;
@@ -2553,7 +2554,7 @@ export class Php8Parser implements Parser {
25532554 return new Name ( token . text , this . endAttributes ( attrs ) ) ;
25542555 }
25552556 // PHP 8.0+: keywords can be used as namespace names (e.g., namespace fn; fn\use())
2556- if ( this . isSemiReservedKeyword ( ) || token . id === T . T_SELF || token . id === T . T_PARENT || token . id === T . T_STATIC ) {
2557+ if ( this . isSemiReservedKeyword ( ) || token . id === T . T_STATIC || ( token . id === T . T_STRING && ( token . text === 'self' || token . text === 'parent' ) ) ) {
25572558 this . advance ( ) ;
25582559 return new Name ( token . text , this . endAttributes ( attrs ) ) ;
25592560 }
@@ -2697,7 +2698,7 @@ export class Php8Parser implements Parser {
26972698 return args ;
26982699 }
26992700
2700- private parseNew ( ) : Node {
2701+ private parseNew ( ) : Expr {
27012702 const attrs = this . startAttributes ( ) ;
27022703 this . expect ( T . T_NEW ) ;
27032704 if ( this . is ( T . T_CLASS ) ) {
@@ -2732,7 +2733,7 @@ export class Php8Parser implements Parser {
27322733 }
27332734 if ( this . is ( T . T_VARIABLE ) ) {
27342735 // Dynamic class: new $a, new $a->b, new $a['b'], new $a::$b
2735- let node : Node = this . parseCallableVariable ( ) ;
2736+ let node : Expr = this . parseCallableVariable ( ) ;
27362737 // Handle property access, array access, static property chains
27372738 while ( true ) {
27382739 if ( this . is ( T . T_OBJECT_OPERATOR ) ) {
@@ -2768,7 +2769,7 @@ export class Php8Parser implements Parser {
27682769 return name ;
27692770 }
27702771
2771- private parseAnonymousClass ( newAttrs : Record < string , any > , flags : number = 0 ) : Node {
2772+ private parseAnonymousClass ( newAttrs : Record < string , any > , flags : number = 0 ) : Expr {
27722773 if ( flags & Modifiers . READONLY ) {
27732774 this . expect ( T . T_READONLY ) ;
27742775 }
@@ -2798,7 +2799,7 @@ export class Php8Parser implements Parser {
27982799 return new New_ ( classNode , args , this . endAttributes ( newAttrs ) ) ;
27992800 }
28002801
2801- private parseArrayLong ( ) : Node {
2802+ private parseArrayLong ( ) : Expr {
28022803 const attrs = this . startAttributes ( ) ;
28032804 this . expect ( T . T_ARRAY ) ;
28042805 this . expect ( '(' . charCodeAt ( 0 ) ) ;
@@ -2807,7 +2808,7 @@ export class Php8Parser implements Parser {
28072808 return new ExprArray_ ( items , this . endAttributes ( attrs ) ) ;
28082809 }
28092810
2810- private parseArrayShort ( ) : Node {
2811+ private parseArrayShort ( ) : Expr {
28112812 const attrs = this . startAttributes ( ) ;
28122813 this . expect ( '[' . charCodeAt ( 0 ) ) ;
28132814 const items = this . parseArrayItemList ( ']' . charCodeAt ( 0 ) ) ;
@@ -2841,7 +2842,7 @@ export class Php8Parser implements Parser {
28412842 return items ;
28422843 }
28432844
2844- private parseList ( ) : Node {
2845+ private parseList ( ) : Expr {
28452846 const attrs = this . startAttributes ( ) ;
28462847 this . expect ( T . T_LIST ) ;
28472848 this . expect ( '(' . charCodeAt ( 0 ) ) ;
@@ -2872,7 +2873,7 @@ export class Php8Parser implements Parser {
28722873 return items ;
28732874 }
28742875
2875- private parseClosure ( attrGroups : Node [ ] = [ ] ) : Node {
2876+ private parseClosure ( attrGroups : Node [ ] = [ ] ) : Expr {
28762877 const attrs = this . startAttributes ( ) ;
28772878 const isStatic = false ;
28782879 this . expect ( T . T_FUNCTION ) ;
@@ -2888,7 +2889,7 @@ export class Php8Parser implements Parser {
28882889 return new Closure ( { static : isStatic , byRef, params, uses, returnType, stmts, attrGroups } , this . endAttributes ( attrs ) ) ;
28892890 }
28902891
2891- private parseStaticClosure ( attrGroups : Node [ ] = [ ] ) : Node {
2892+ private parseStaticClosure ( attrGroups : Node [ ] = [ ] ) : Expr {
28922893 const attrs = this . startAttributes ( ) ;
28932894 this . expect ( T . T_STATIC ) ;
28942895 if ( this . is ( T . T_FN ) ) {
@@ -2907,7 +2908,7 @@ export class Php8Parser implements Parser {
29072908 return new Closure ( { static : true , byRef, params, uses, returnType, stmts, attrGroups } , this . endAttributes ( attrs ) ) ;
29082909 }
29092910
2910- private parseStaticRef ( ) : Node {
2911+ private parseStaticRef ( ) : Expr {
29112912 const attrs = this . startAttributes ( ) ;
29122913 this . advance ( ) ;
29132914 return new Name ( 'static' , this . endAttributes ( attrs ) ) ;
@@ -2928,12 +2929,12 @@ export class Php8Parser implements Parser {
29282929 return uses ;
29292930 }
29302931
2931- private parseArrowFunction ( attrGroups : Node [ ] = [ ] ) : Node {
2932+ private parseArrowFunction ( attrGroups : Node [ ] = [ ] ) : Expr {
29322933 const attrs = this . startAttributes ( ) ;
29332934 return this . parseArrowFunctionInner ( false , attrs , attrGroups ) ;
29342935 }
29352936
2936- private parseArrowFunctionInner ( isStatic : boolean , attrs : Record < string , any > , attrGroups : Node [ ] = [ ] ) : Node {
2937+ private parseArrowFunctionInner ( isStatic : boolean , attrs : Record < string , any > , attrGroups : Node [ ] = [ ] ) : Expr {
29372938 this . expect ( T . T_FN ) ;
29382939 const byRef = ! ! this . eat ( '&' . charCodeAt ( 0 ) ) || ! ! this . eat ( T . T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG ) || ! ! this . eat ( T . T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG ) ;
29392940 this . expect ( '(' . charCodeAt ( 0 ) ) ;
@@ -2945,11 +2946,11 @@ export class Php8Parser implements Parser {
29452946 return new ArrowFunction ( { static : isStatic , byRef, params, returnType, expr : body , attrGroups } , this . endAttributes ( attrs ) ) ;
29462947 }
29472948
2948- private parseYield ( ) : Node {
2949+ private parseYield ( ) : Expr {
29492950 const attrs = this . startAttributes ( ) ;
29502951 this . expect ( T . T_YIELD ) ;
2951- let key : Node | null = null ;
2952- let value : Node | null = null ;
2952+ let key : Expr | null = null ;
2953+ let value : Expr | null = null ;
29532954 if ( this . canStartExpression ( ) ) {
29542955 // yield binds at assignment level - doesn't consume and/or/xor
29552956 value = this . parseExpression ( 4 ) ;
@@ -2961,15 +2962,15 @@ export class Php8Parser implements Parser {
29612962 return new Yield_ ( value , key , this . endAttributes ( attrs ) ) ;
29622963 }
29632964
2964- private parseYieldFrom ( ) : Node {
2965+ private parseYieldFrom ( ) : Expr {
29652966 const attrs = this . startAttributes ( ) ;
29662967 this . advance ( ) ;
29672968 // yield from binds at assignment level
29682969 const expr = this . parseExpression ( 4 ) ;
29692970 return new YieldFrom ( expr , this . endAttributes ( attrs ) ) ;
29702971 }
29712972
2972- private parseInclude ( ) : Node {
2973+ private parseInclude ( ) : Expr {
29732974 const attrs = this . startAttributes ( ) ;
29742975 const token = this . advance ( ) ;
29752976 const expr = this . parseExpression ( ) ;
@@ -2984,7 +2985,7 @@ export class Php8Parser implements Parser {
29842985 return new Include_ ( expr , type , this . endAttributes ( attrs ) ) ;
29852986 }
29862987
2987- private parseMatch ( ) : Node {
2988+ private parseMatch ( ) : Expr {
29882989 const attrs = this . startAttributes ( ) ;
29892990 this . expect ( T . T_MATCH ) ;
29902991 this . expect ( '(' . charCodeAt ( 0 ) ) ;
@@ -3013,7 +3014,7 @@ export class Php8Parser implements Parser {
30133014 return new Match_ ( cond , arms , this . endAttributes ( attrs ) ) ;
30143015 }
30153016
3016- private parseShellExec ( ) : Node {
3017+ private parseShellExec ( ) : Expr {
30173018 const attrs = this . startAttributes ( ) ;
30183019 this . expect ( '`' . charCodeAt ( 0 ) ) ;
30193020 const savedQuote = this . encapsedQuoteChar ;
@@ -3032,7 +3033,7 @@ export class Php8Parser implements Parser {
30323033 return new ShellExec ( parts , this . endAttributes ( attrs ) ) ;
30333034 }
30343035
3035- private parseDollarOpenCurlyBraces ( ) : Node {
3036+ private parseDollarOpenCurlyBraces ( ) : Expr {
30363037 // Handle ${name} and ${expr} syntax
30373038 this . advance ( ) ; // consume T_DOLLAR_OPEN_CURLY_BRACES
30383039 if ( this . is ( T . T_STRING_VARNAME ) ) {
@@ -3108,12 +3109,12 @@ export class Php8Parser implements Parser {
31083109 return result ;
31093110 }
31103111
3111- private parseEncapsedPart ( ) : Node | null {
3112+ private parseEncapsedPart ( ) : Expr | null {
31123113 if ( this . is ( T . T_ENCAPSED_AND_WHITESPACE ) ) {
31133114 const token = this . advance ( ) ;
31143115 return new InterpolatedStringPart ( this . processDoubleQuotedEscapes ( token . text , this . encapsedQuoteChar ) ) ;
31153116 } else if ( this . is ( T . T_VARIABLE ) ) {
3116- let node : Node = this . parseSimpleVariable ( ) ;
3117+ let node : Expr = this . parseSimpleVariable ( ) ;
31173118 // Handle $var->prop and $var?->prop in encapsed strings
31183119 if ( this . is ( T . T_OBJECT_OPERATOR ) ) {
31193120 this . advance ( ) ;
@@ -3127,7 +3128,7 @@ export class Php8Parser implements Parser {
31273128 // Handle $var[expr] array access in encapsed strings
31283129 if ( this . is ( '[' . charCodeAt ( 0 ) ) ) {
31293130 this . advance ( ) ;
3130- let dim : Node | null = null ;
3131+ let dim : Expr | null = null ;
31313132 if ( this . is ( T . T_STRING ) ) {
31323133 // String key (bareword)
31333134 dim = new ScalarString ( this . advance ( ) . text , { } ) ;
@@ -3170,7 +3171,7 @@ export class Php8Parser implements Parser {
31703171 return null ;
31713172 }
31723173
3173- private parseInterpolatedString ( ) : Node {
3174+ private parseInterpolatedString ( ) : Expr {
31743175 const attrs = this . startAttributes ( ) ;
31753176 this . expect ( '"' . charCodeAt ( 0 ) ) ;
31763177 const parts : Node [ ] = [ ] ;
@@ -3186,7 +3187,7 @@ export class Php8Parser implements Parser {
31863187 return new InterpolatedString ( parts , this . endAttributes ( attrs ) ) ;
31873188 }
31883189
3189- private parseHeredoc ( ) : Node {
3190+ private parseHeredoc ( ) : Expr {
31903191 const attrs = this . startAttributes ( ) ;
31913192 const startToken = this . advance ( ) ; // T_START_HEREDOC
31923193 const isNowdoc = startToken . text . includes ( "'" ) ;
0 commit comments