php8.1: added intersection types support (#29)
This commit is contained in:
parent
7f6cd25376
commit
e16671724e
@ -789,6 +789,24 @@ func (b *Builder) NewUnionType(
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) NewIntersectionType(
|
||||
Types ast.Vertex,
|
||||
) *ast.Intersection {
|
||||
var types []ast.Vertex
|
||||
var sepTkns []*token.Token
|
||||
if Types != nil {
|
||||
cases := Types.(*ParserSeparatedList)
|
||||
types = cases.Items
|
||||
sepTkns = cases.SeparatorTkns
|
||||
}
|
||||
|
||||
return &ast.Intersection{
|
||||
Position: b.Pos.NewNodeListPosition(types),
|
||||
Types: types,
|
||||
SeparatorTkns: sepTkns,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) NewReturnType(
|
||||
ColonTkn *token.Token,
|
||||
Type ast.Vertex,
|
||||
|
@ -227,6 +227,21 @@ func (lex *Lexer) ungetCnt(n int) {
|
||||
lex.te = lex.te - n
|
||||
}
|
||||
|
||||
func (lex *Lexer) ungetWhile(s byte) {
|
||||
for i := 0; i < 100; i++ {
|
||||
v := lex.data[lex.te]
|
||||
if v == s {
|
||||
break
|
||||
}
|
||||
|
||||
lex.te--
|
||||
lex.p--
|
||||
}
|
||||
|
||||
lex.te++
|
||||
lex.p++
|
||||
}
|
||||
|
||||
func (lex *Lexer) error(msg string) {
|
||||
if lex.errHandlerFunc == nil {
|
||||
return
|
||||
|
@ -322,3 +322,114 @@ enum C: int implements Bar {}
|
||||
|
||||
suite.Run()
|
||||
}
|
||||
|
||||
func TestIntersectionTypes(t *testing.T) {
|
||||
suite := tester.NewParserDumpTestSuite(t)
|
||||
suite.UsePHP8()
|
||||
suite.Code = `<?php
|
||||
class Test {
|
||||
public A&B $prop;
|
||||
}
|
||||
|
||||
function test(A&B $a): A&B {}
|
||||
`
|
||||
|
||||
suite.Expected = `&ast.Root{
|
||||
Stmts: []ast.Vertex{
|
||||
&ast.StmtClass{
|
||||
Name: &ast.Identifier{
|
||||
Val: []byte("Test"),
|
||||
},
|
||||
Stmts: []ast.Vertex{
|
||||
&ast.StmtPropertyList{
|
||||
Modifiers: []ast.Vertex{
|
||||
&ast.Identifier{
|
||||
Val: []byte("public"),
|
||||
},
|
||||
},
|
||||
Type: &ast.Intersection{
|
||||
Types: []ast.Vertex{
|
||||
&ast.Name{
|
||||
Parts: []ast.Vertex{
|
||||
&ast.NamePart{
|
||||
Val: []byte("A"),
|
||||
},
|
||||
},
|
||||
},
|
||||
&ast.Name{
|
||||
Parts: []ast.Vertex{
|
||||
&ast.NamePart{
|
||||
Val: []byte("B"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Props: []ast.Vertex{
|
||||
&ast.StmtProperty{
|
||||
Var: &ast.ExprVariable{
|
||||
Name: &ast.Identifier{
|
||||
Val: []byte("$prop"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&ast.StmtFunction{
|
||||
Name: &ast.Identifier{
|
||||
Val: []byte("test"),
|
||||
},
|
||||
Params: []ast.Vertex{
|
||||
&ast.Parameter{
|
||||
Type: &ast.Intersection{
|
||||
Types: []ast.Vertex{
|
||||
&ast.Name{
|
||||
Parts: []ast.Vertex{
|
||||
&ast.NamePart{
|
||||
Val: []byte("A"),
|
||||
},
|
||||
},
|
||||
},
|
||||
&ast.Name{
|
||||
Parts: []ast.Vertex{
|
||||
&ast.NamePart{
|
||||
Val: []byte("B"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Var: &ast.ExprVariable{
|
||||
Name: &ast.Identifier{
|
||||
Val: []byte("$a"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ReturnType: &ast.Intersection{
|
||||
Types: []ast.Vertex{
|
||||
&ast.Name{
|
||||
Parts: []ast.Vertex{
|
||||
&ast.NamePart{
|
||||
Val: []byte("A"),
|
||||
},
|
||||
},
|
||||
},
|
||||
&ast.Name{
|
||||
Parts: []ast.Vertex{
|
||||
&ast.NamePart{
|
||||
Val: []byte("B"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Stmts: []ast.Vertex{},
|
||||
},
|
||||
},
|
||||
},`
|
||||
|
||||
suite.Run()
|
||||
}
|
||||
|
@ -4177,7 +4177,7 @@ class Point {
|
||||
},
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.ID(57492),
|
||||
Val: []byte("&"),
|
||||
FreeFloating: []*token.Token{
|
||||
{
|
||||
@ -4302,7 +4302,7 @@ class Point {
|
||||
},
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.ID(57492),
|
||||
Val: []byte("&"),
|
||||
FreeFloating: []*token.Token{
|
||||
{
|
||||
|
@ -1972,7 +1972,7 @@ func TestPhp8ParameterNode(t *testing.T) {
|
||||
},
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 2,
|
||||
@ -2453,7 +2453,7 @@ func TestPhp8ParameterNode(t *testing.T) {
|
||||
},
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 3,
|
||||
@ -2826,7 +2826,7 @@ func TestPhp8ParameterNode(t *testing.T) {
|
||||
},
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 4,
|
||||
@ -3212,7 +3212,7 @@ func TestPhp8ParameterNode(t *testing.T) {
|
||||
},
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 5,
|
||||
@ -10102,7 +10102,7 @@ func TestStmtClassMethod_Php8ClassMethod(t *testing.T) {
|
||||
},
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 1,
|
||||
@ -16870,7 +16870,7 @@ func TestStmtForeach_WithRef(t *testing.T) {
|
||||
},
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 1,
|
||||
@ -18087,7 +18087,7 @@ func TestStmtFunction_Ref(t *testing.T) {
|
||||
},
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 1,
|
||||
@ -18309,7 +18309,7 @@ func TestStmtFunction_ReturnType(t *testing.T) {
|
||||
},
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 1,
|
||||
@ -35439,7 +35439,7 @@ func TestExprArray_Items(t *testing.T) {
|
||||
EndPos: 18,
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 1,
|
||||
@ -35929,7 +35929,7 @@ func TestExprArrowFunction_ReturnType(t *testing.T) {
|
||||
},
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 1,
|
||||
@ -37273,7 +37273,7 @@ func TestExprClosure_Use(t *testing.T) {
|
||||
EndPos: 32,
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 1,
|
||||
@ -37633,7 +37633,7 @@ func TestExprClosure_Use2(t *testing.T) {
|
||||
EndPos: 28,
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 1,
|
||||
@ -45056,7 +45056,7 @@ func TestExprShortArray_Items(t *testing.T) {
|
||||
EndPos: 13,
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 1,
|
||||
@ -49734,7 +49734,7 @@ func TestExprAssign_Assign(t *testing.T) {
|
||||
},
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 3,
|
||||
@ -49870,7 +49870,7 @@ func TestExprAssign_Assign(t *testing.T) {
|
||||
},
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 4,
|
||||
@ -50038,7 +50038,7 @@ func TestExprAssign_Assign(t *testing.T) {
|
||||
},
|
||||
},
|
||||
AmpersandTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 5,
|
||||
@ -51946,7 +51946,7 @@ func TestExprBinary_BitwiseAnd(t *testing.T) {
|
||||
},
|
||||
},
|
||||
OpTkn: &token.Token{
|
||||
ID: token.ID(38),
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
Position: &position.Position{
|
||||
StartLine: 2,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -162,6 +162,8 @@ import (
|
||||
%token <token> T_NAME_FULLY_QUALIFIED
|
||||
%token <token> T_READONLY
|
||||
%token <token> T_ENUM
|
||||
%token <token> T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
|
||||
%token <token> T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG
|
||||
%token <token> '"'
|
||||
%token <token> '`'
|
||||
%token <token> '{'
|
||||
@ -208,7 +210,7 @@ import (
|
||||
%left T_BOOLEAN_AND
|
||||
%left '|'
|
||||
%left '^'
|
||||
%left '&'
|
||||
%left T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG
|
||||
%nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL T_SPACESHIP
|
||||
%nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL
|
||||
%left '.'
|
||||
@ -227,7 +229,7 @@ import (
|
||||
%left T_ENDIF
|
||||
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC T_READONLY
|
||||
|
||||
%type <token> optional_arg_ref optional_ellipsis returns_ref
|
||||
%type <token> optional_ref optional_arg_ref optional_ellipsis
|
||||
|
||||
%type <token> reserved_non_modifiers
|
||||
%type <token> semi_reserved
|
||||
@ -236,6 +238,7 @@ import (
|
||||
%type <token> optional_comma
|
||||
%type <token> case_separator
|
||||
%type <token> use_type
|
||||
%type <token> ampersand
|
||||
|
||||
%type <node> top_statement name statement function_declaration_statement
|
||||
%type <node> class_declaration_statement trait_declaration_statement
|
||||
@ -267,6 +270,7 @@ import (
|
||||
|
||||
%type <node> type_expr type union_type optional_return_type
|
||||
%type <node> type_expr_without_static type_without_static union_type_without_static optional_type_without_static
|
||||
%type <node> intersection_type intersection_type_without_static
|
||||
|
||||
%type <node> class_modifier
|
||||
%type <node> argument_list ctor_arguments
|
||||
@ -336,6 +340,11 @@ semi_reserved:
|
||||
| T_PROTECTED {$$=$1} | T_PUBLIC {$$=$1} | T_READONLY {$$=$1}
|
||||
;
|
||||
|
||||
ampersand:
|
||||
T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG { $$ = $1 }
|
||||
| T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG { $$ = $1 }
|
||||
;
|
||||
|
||||
identifier:
|
||||
T_STRING { $$ = $1 }
|
||||
;
|
||||
@ -839,16 +848,21 @@ unset_variable:
|
||||
;
|
||||
|
||||
function_declaration_statement:
|
||||
T_FUNCTION returns_ref identifier '(' parameter_list ')' optional_return_type '{' inner_statement_list '}'
|
||||
T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type '{' inner_statement_list '}'
|
||||
{ $$ = yylex.(*Parser).builder.NewFunction(nil, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10) }
|
||||
| attributes
|
||||
T_FUNCTION returns_ref identifier '(' parameter_list ')' optional_return_type '{' inner_statement_list '}'
|
||||
T_FUNCTION optional_ref identifier '(' parameter_list ')' optional_return_type '{' inner_statement_list '}'
|
||||
{ $$ = yylex.(*Parser).builder.NewFunction($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) }
|
||||
;
|
||||
|
||||
optional_ref:
|
||||
/* empty */ { $$ = nil }
|
||||
| ampersand { $$ = $1 }
|
||||
;
|
||||
|
||||
optional_arg_ref:
|
||||
/* empty */ { $$ = nil }
|
||||
| '&' { $$ = $1 }
|
||||
| T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG { $$ = $1 }
|
||||
;
|
||||
|
||||
optional_ellipsis:
|
||||
@ -956,7 +970,7 @@ foreach_variable:
|
||||
{
|
||||
$$ = $1
|
||||
}
|
||||
| '&' variable
|
||||
| ampersand variable
|
||||
{
|
||||
$$ = &ast.StmtForeach{
|
||||
Position: yylex.(*Parser).builder.Pos.NewTokenNodePosition($1, $2),
|
||||
@ -1299,6 +1313,7 @@ type_expr:
|
||||
type { $$ = $1 }
|
||||
| '?' type { $$ = yylex.(*Parser).builder.NewNullableType($1, $2) }
|
||||
| union_type { $$ = yylex.(*Parser).builder.NewUnionType($1) }
|
||||
| intersection_type { $$ = yylex.(*Parser).builder.NewIntersectionType($1) }
|
||||
;
|
||||
|
||||
type:
|
||||
@ -1324,10 +1339,25 @@ union_type_without_static:
|
||||
{ $$ = yylex.(*Parser).builder.AppendToSeparatedList($1, $2, $3) }
|
||||
;
|
||||
|
||||
intersection_type:
|
||||
type T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type
|
||||
{ $$ = yylex.(*Parser).builder.NewSeparatedListWithTwoElements($1, $2, $3) }
|
||||
| intersection_type T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type
|
||||
{ $$ = yylex.(*Parser).builder.AppendToSeparatedList($1, $2, $3) }
|
||||
;
|
||||
|
||||
intersection_type_without_static:
|
||||
type_expr_without_static T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type_expr_without_static
|
||||
{ $$ = yylex.(*Parser).builder.NewSeparatedListWithTwoElements($1, $2, $3) }
|
||||
| intersection_type_without_static T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG type_expr_without_static
|
||||
{ $$ = yylex.(*Parser).builder.AppendToSeparatedList($1, $2, $3) }
|
||||
;
|
||||
|
||||
type_expr_without_static:
|
||||
type_without_static { $$ = $1 }
|
||||
| '?' type_without_static { $$ = yylex.(*Parser).builder.NewNullableType($1, $2) }
|
||||
| union_type_without_static { $$ = yylex.(*Parser).builder.NewUnionType($1) }
|
||||
| intersection_type_without_static { $$ = yylex.(*Parser).builder.NewIntersectionType($1) }
|
||||
;
|
||||
|
||||
optional_type_without_static:
|
||||
@ -1438,7 +1468,7 @@ class_statement:
|
||||
{ $$ = yylex.(*Parser).builder.NewPropertyList($1, $2, $3, $4, $5) }
|
||||
| optional_attributes method_modifiers T_CONST class_const_list ';'
|
||||
{ $$ = yylex.(*Parser).builder.NewClassConstList($1, $2, $3, $4, $5) }
|
||||
| optional_attributes method_modifiers T_FUNCTION returns_ref identifier_ex '(' parameter_list ')' optional_return_type method_body
|
||||
| optional_attributes method_modifiers T_FUNCTION optional_ref identifier_ex '(' parameter_list ')' optional_return_type method_body
|
||||
{ $$ = yylex.(*Parser).builder.NewClassMethod($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) }
|
||||
| T_USE name_list trait_adaptations
|
||||
{
|
||||
@ -1889,7 +1919,7 @@ expr_without_variable:
|
||||
Expr: $3,
|
||||
}
|
||||
}
|
||||
| variable '=' '&' expr
|
||||
| variable '=' ampersand expr
|
||||
{
|
||||
$$ = &ast.ExprAssignReference{
|
||||
Position: yylex.(*Parser).builder.Pos.NewNodesPosition($1, $4),
|
||||
@ -2110,7 +2140,16 @@ expr_without_variable:
|
||||
Right: $3,
|
||||
}
|
||||
}
|
||||
| expr '&' expr
|
||||
| expr T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG expr
|
||||
{
|
||||
$$ = &ast.ExprBinaryBitwiseAnd{
|
||||
Position: yylex.(*Parser).builder.Pos.NewNodesPosition($1, $3),
|
||||
Left: $1,
|
||||
OpTkn: $2,
|
||||
Right: $3,
|
||||
}
|
||||
}
|
||||
| expr T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG expr
|
||||
{
|
||||
$$ = &ast.ExprBinaryBitwiseAnd{
|
||||
Position: yylex.(*Parser).builder.Pos.NewNodesPosition($1, $3),
|
||||
@ -2558,7 +2597,7 @@ attributed_inline_function:
|
||||
;
|
||||
|
||||
inline_function:
|
||||
T_FUNCTION returns_ref backup_doc_comment '(' parameter_list ')' lexical_vars optional_return_type '{' inner_statement_list '}'
|
||||
T_FUNCTION optional_ref backup_doc_comment '(' parameter_list ')' lexical_vars optional_return_type '{' inner_statement_list '}'
|
||||
{
|
||||
closure := $7.(*ast.ExprClosure)
|
||||
|
||||
@ -2577,7 +2616,7 @@ inline_function:
|
||||
|
||||
$$ = closure
|
||||
}
|
||||
| T_FN returns_ref '(' parameter_list ')' optional_return_type backup_doc_comment T_DOUBLE_ARROW expr %prec T_THROW
|
||||
| T_FN optional_ref '(' parameter_list ')' optional_return_type backup_doc_comment T_DOUBLE_ARROW expr %prec T_THROW
|
||||
{
|
||||
$$ = &ast.ExprArrowFunction{
|
||||
Position: yylex.(*Parser).builder.Pos.NewTokenNodePosition($1, $9),
|
||||
@ -2599,11 +2638,6 @@ backup_doc_comment:
|
||||
/* empty */
|
||||
;
|
||||
|
||||
returns_ref:
|
||||
/* empty */ { $$ = nil }
|
||||
| '&' { $$ = $1 }
|
||||
;
|
||||
|
||||
lexical_vars:
|
||||
/* empty */
|
||||
{ $$ = &ast.ExprClosure{} }
|
||||
@ -2643,7 +2677,7 @@ lexical_var:
|
||||
},
|
||||
}
|
||||
}
|
||||
| '&' plain_variable
|
||||
| ampersand plain_variable
|
||||
{
|
||||
$$ = &ast.ExprClosureUse{
|
||||
Position: yylex.(*Parser).builder.Pos.NewTokensPosition($1, $2),
|
||||
@ -3105,7 +3139,7 @@ array_pair:
|
||||
Val: $1,
|
||||
}
|
||||
}
|
||||
| expr T_DOUBLE_ARROW '&' variable
|
||||
| expr T_DOUBLE_ARROW T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG variable
|
||||
{
|
||||
$$ = &ast.ExprArrayItem{
|
||||
Position: yylex.(*Parser).builder.Pos.NewNodesPosition($1, $4),
|
||||
@ -3115,7 +3149,7 @@ array_pair:
|
||||
Val: $4,
|
||||
}
|
||||
}
|
||||
| '&' variable
|
||||
| T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG variable
|
||||
{
|
||||
$$ = &ast.ExprArrayItem{
|
||||
Position: yylex.(*Parser).builder.Pos.NewTokenNodePosition($1, $2),
|
||||
|
38727
internal/php8/scanner.go
38727
internal/php8/scanner.go
File diff suppressed because it is too large
Load Diff
@ -67,7 +67,7 @@ func (lex *Lexer) Lex() *token.Token {
|
||||
varname_second = varname_first | [0-9];
|
||||
varname = varname_first (varname_second)*;
|
||||
heredoc_label = varname >heredoc_lbl_start %heredoc_lbl_end;
|
||||
operators = ';'|':'|','|'.'|'['|']'|'('|')'|'|'|'/'|'^'|'&'|'+'|'-'|'*'|'='|'%'|'!'|'~'|'$'|'<'|'>'|'?'|'@';
|
||||
operators = ';'|':'|','|'.'|'['|']'|'('|')'|'|'|'/'|'^'|'+'|'-'|'*'|'='|'%'|'!'|'~'|'$'|'<'|'>'|'?'|'@';
|
||||
|
||||
prepush { lex.growCallStack(); }
|
||||
|
||||
@ -295,7 +295,6 @@ func (lex *Lexer) Lex() *token.Token {
|
||||
'or'i => {lex.setTokenPosition(tkn); tok = token.T_LOGICAL_OR; fbreak;};
|
||||
'xor'i => {lex.setTokenPosition(tkn); tok = token.T_LOGICAL_XOR; fbreak;};
|
||||
'#[' => {lex.setTokenPosition(tkn); tok = token.T_ATTRIBUTE; fbreak;};
|
||||
'...' => {lex.setTokenPosition(tkn); tok = token.T_ELLIPSIS; fbreak;};
|
||||
'::' => {lex.setTokenPosition(tkn); tok = token.T_PAAMAYIM_NEKUDOTAYIM; fbreak;};
|
||||
'&&' => {lex.setTokenPosition(tkn); tok = token.T_BOOLEAN_AND; fbreak;};
|
||||
'||' => {lex.setTokenPosition(tkn); tok = token.T_BOOLEAN_OR; fbreak;};
|
||||
@ -370,6 +369,12 @@ func (lex *Lexer) Lex() *token.Token {
|
||||
fbreak;
|
||||
};
|
||||
|
||||
"&" whitespace_line* '$' => { lex.ungetWhile('&'); lex.setTokenPosition(tkn); tok = token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG; fbreak; };
|
||||
"&" whitespace_line* '...' => { lex.ungetWhile('&'); lex.setTokenPosition(tkn); tok = token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG; fbreak; };
|
||||
"&" whitespace_line* ^'$' => { lex.ungetWhile('&'); lex.setTokenPosition(tkn); tok = token.T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG; fbreak; };
|
||||
"&" whitespace_line* ^'...' => { lex.ungetWhile('&'); lex.setTokenPosition(tkn); tok = token.T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG; fbreak; };
|
||||
|
||||
'...' => { lex.setTokenPosition(tkn); tok = token.T_ELLIPSIS; fbreak; };
|
||||
"{" => { lex.setTokenPosition(tkn); tok = token.ID(int('{')); lex.call(ftargs, fentry(php)); goto _out; };
|
||||
"}" => { lex.setTokenPosition(tkn); tok = token.ID(int('}')); lex.ret(1); goto _out;};
|
||||
"$" varname => { lex.setTokenPosition(tkn); tok = token.T_VARIABLE; fbreak; };
|
||||
|
@ -180,3 +180,192 @@ func TestEnumTokens(t *testing.T) {
|
||||
}
|
||||
suite.Run()
|
||||
}
|
||||
|
||||
func TestAmpersandFollowedByEllipsisTokens(t *testing.T) {
|
||||
suite := tester.NewLexerTokenStructTestSuite(t)
|
||||
suite.UsePHP8()
|
||||
suite.Code = "<?php &...;"
|
||||
suite.Expected = []*token.Token{
|
||||
{
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
},
|
||||
{
|
||||
ID: token.T_ELLIPSIS,
|
||||
Value: []byte("..."),
|
||||
},
|
||||
{
|
||||
ID: ';',
|
||||
Value: []byte(";"),
|
||||
},
|
||||
}
|
||||
suite.Run()
|
||||
}
|
||||
|
||||
func TestAmpersandFollowedByEllipsisTokens2(t *testing.T) {
|
||||
suite := tester.NewLexerTokenStructTestSuite(t)
|
||||
suite.UsePHP8()
|
||||
suite.Code = "<?php & ...;"
|
||||
suite.Expected = []*token.Token{
|
||||
{
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
},
|
||||
{
|
||||
ID: token.T_ELLIPSIS,
|
||||
Value: []byte("..."),
|
||||
},
|
||||
{
|
||||
ID: ';',
|
||||
Value: []byte(";"),
|
||||
},
|
||||
}
|
||||
suite.Run()
|
||||
}
|
||||
|
||||
func TestAmpersandFollowedByEllipsisTokens3(t *testing.T) {
|
||||
suite := tester.NewLexerTokenStructTestSuite(t)
|
||||
suite.UsePHP8()
|
||||
suite.Code = "<?php & \n\t ...;"
|
||||
suite.Expected = []*token.Token{
|
||||
{
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
},
|
||||
{
|
||||
ID: token.T_ELLIPSIS,
|
||||
Value: []byte("..."),
|
||||
},
|
||||
{
|
||||
ID: ';',
|
||||
Value: []byte(";"),
|
||||
},
|
||||
}
|
||||
suite.Run()
|
||||
}
|
||||
|
||||
func TestAmpersandFollowedByVarTokens(t *testing.T) {
|
||||
suite := tester.NewLexerTokenStructTestSuite(t)
|
||||
suite.UsePHP8()
|
||||
suite.Code = "<?php &$a;"
|
||||
suite.Expected = []*token.Token{
|
||||
{
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
},
|
||||
{
|
||||
ID: token.T_VARIABLE,
|
||||
Value: []byte("$a"),
|
||||
},
|
||||
{
|
||||
ID: ';',
|
||||
Value: []byte(";"),
|
||||
},
|
||||
}
|
||||
suite.Run()
|
||||
}
|
||||
|
||||
func TestAmpersandFollowedByVarTokens2(t *testing.T) {
|
||||
suite := tester.NewLexerTokenStructTestSuite(t)
|
||||
suite.UsePHP8()
|
||||
suite.Code = "<?php & $a;"
|
||||
suite.Expected = []*token.Token{
|
||||
{
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
},
|
||||
{
|
||||
ID: token.T_VARIABLE,
|
||||
Value: []byte("$a"),
|
||||
},
|
||||
{
|
||||
ID: ';',
|
||||
Value: []byte(";"),
|
||||
},
|
||||
}
|
||||
suite.Run()
|
||||
}
|
||||
|
||||
func TestAmpersandFollowedByVarTokens3(t *testing.T) {
|
||||
suite := tester.NewLexerTokenStructTestSuite(t)
|
||||
suite.UsePHP8()
|
||||
suite.Code = "<?php & \n\t $a;"
|
||||
suite.Expected = []*token.Token{
|
||||
{
|
||||
ID: token.T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
},
|
||||
{
|
||||
ID: token.T_VARIABLE,
|
||||
Value: []byte("$a"),
|
||||
},
|
||||
{
|
||||
ID: ';',
|
||||
Value: []byte(";"),
|
||||
},
|
||||
}
|
||||
suite.Run()
|
||||
}
|
||||
|
||||
func TestAmpersandNotFollowedByVarOrEllipsisTokens(t *testing.T) {
|
||||
suite := tester.NewLexerTokenStructTestSuite(t)
|
||||
suite.UsePHP8()
|
||||
suite.Code = "<?php &A;"
|
||||
suite.Expected = []*token.Token{
|
||||
{
|
||||
ID: token.T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
},
|
||||
{
|
||||
ID: token.T_STRING,
|
||||
Value: []byte("A"),
|
||||
},
|
||||
{
|
||||
ID: ';',
|
||||
Value: []byte(";"),
|
||||
},
|
||||
}
|
||||
suite.Run()
|
||||
}
|
||||
|
||||
func TestAmpersandNotFollowedByVarOrEllipsisTokens2(t *testing.T) {
|
||||
suite := tester.NewLexerTokenStructTestSuite(t)
|
||||
suite.UsePHP8()
|
||||
suite.Code = "<?php & A;"
|
||||
suite.Expected = []*token.Token{
|
||||
{
|
||||
ID: token.T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
},
|
||||
{
|
||||
ID: token.T_STRING,
|
||||
Value: []byte("A"),
|
||||
},
|
||||
{
|
||||
ID: ';',
|
||||
Value: []byte(";"),
|
||||
},
|
||||
}
|
||||
suite.Run()
|
||||
}
|
||||
|
||||
func TestAmpersandNotFollowedByVarOrEllipsisTokens3(t *testing.T) {
|
||||
suite := tester.NewLexerTokenStructTestSuite(t)
|
||||
suite.UsePHP8()
|
||||
suite.Code = "<?php & \n\t A;"
|
||||
suite.Expected = []*token.Token{
|
||||
{
|
||||
ID: token.T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
Value: []byte("&"),
|
||||
},
|
||||
{
|
||||
ID: token.T_STRING,
|
||||
Value: []byte("A"),
|
||||
},
|
||||
{
|
||||
ID: ';',
|
||||
Value: []byte(";"),
|
||||
},
|
||||
}
|
||||
suite.Run()
|
||||
}
|
||||
|
@ -317,7 +317,7 @@ func TestTokens(t *testing.T) {
|
||||
token.ID(int('|')).String(),
|
||||
token.ID(int('/')).String(),
|
||||
token.ID(int('^')).String(),
|
||||
token.ID(int('&')).String(),
|
||||
token.ID(T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG).String(),
|
||||
token.ID(int('+')).String(),
|
||||
token.ID(int('-')).String(),
|
||||
token.ID(int('*')).String(),
|
||||
|
@ -15,6 +15,7 @@ type Visitor interface {
|
||||
Argument(n *Argument)
|
||||
MatchArm(n *MatchArm)
|
||||
Union(n *Union)
|
||||
Intersection(n *Intersection)
|
||||
Attribute(n *Attribute)
|
||||
AttributeGroup(n *AttributeGroup)
|
||||
|
||||
|
@ -50,6 +50,21 @@ func (n *Union) GetPosition() *position.Position {
|
||||
return n.Position
|
||||
}
|
||||
|
||||
// Intersection node is Expr&Expr1&...
|
||||
type Intersection struct {
|
||||
Position *position.Position
|
||||
Types []Vertex
|
||||
SeparatorTkns []*token.Token
|
||||
}
|
||||
|
||||
func (n *Intersection) Accept(v Visitor) {
|
||||
v.Intersection(n)
|
||||
}
|
||||
|
||||
func (n *Intersection) GetPosition() *position.Position {
|
||||
return n.Position
|
||||
}
|
||||
|
||||
// Parameter node
|
||||
type Parameter struct {
|
||||
Position *position.Position
|
||||
|
@ -152,6 +152,8 @@ const (
|
||||
T_NAME_FULLY_QUALIFIED
|
||||
T_READONLY
|
||||
T_ENUM
|
||||
T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
|
||||
T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
|
@ -154,11 +154,13 @@ func _() {
|
||||
_ = x[T_NAME_FULLY_QUALIFIED-57489]
|
||||
_ = x[T_READONLY-57490]
|
||||
_ = x[T_ENUM-57491]
|
||||
_ = x[T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG-57492]
|
||||
_ = x[T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG-57493]
|
||||
}
|
||||
|
||||
const _ID_name = "T_INCLUDET_INCLUDE_ONCET_EXITT_IFT_LNUMBERT_DNUMBERT_STRINGT_STRING_VARNAMET_VARIABLET_NUM_STRINGT_INLINE_HTMLT_CHARACTERT_BAD_CHARACTERT_ENCAPSED_AND_WHITESPACET_CONSTANT_ENCAPSED_STRINGT_ECHOT_DOT_WHILET_ENDWHILET_FORT_ENDFORT_FOREACHT_ENDFOREACHT_DECLARET_ENDDECLARET_AST_SWITCHT_ENDSWITCHT_CASET_DEFAULTT_BREAKT_CONTINUET_GOTOT_FUNCTIONT_FNT_CONSTT_RETURNT_TRYT_CATCHT_FINALLYT_THROWT_USET_INSTEADOFT_GLOBALT_VART_UNSETT_ISSETT_EMPTYT_HALT_COMPILERT_CLASST_TRAITT_INTERFACET_EXTENDST_IMPLEMENTST_OBJECT_OPERATORT_DOUBLE_ARROWT_LISTT_ARRAYT_CALLABLET_CLASS_CT_TRAIT_CT_METHOD_CT_FUNC_CT_LINET_FILET_COMMENTT_DOC_COMMENTT_OPEN_TAGT_OPEN_TAG_WITH_ECHOT_CLOSE_TAGT_WHITESPACET_START_HEREDOCT_END_HEREDOCT_DOLLAR_OPEN_CURLY_BRACEST_CURLY_OPENT_PAAMAYIM_NEKUDOTAYIMT_NAMESPACET_NS_CT_DIRT_NS_SEPARATORT_ELLIPSIST_EVALT_REQUIRET_REQUIRE_ONCET_LOGICAL_ORT_LOGICAL_XORT_LOGICAL_ANDT_INSTANCEOFT_NEWT_CLONET_ELSEIFT_ELSET_ENDIFT_PRINTT_YIELDT_STATICT_ABSTRACTT_FINALT_PRIVATET_PROTECTEDT_PUBLICT_INCT_DECT_YIELD_FROMT_INT_CASTT_DOUBLE_CASTT_STRING_CASTT_ARRAY_CASTT_OBJECT_CASTT_BOOL_CASTT_UNSET_CASTT_COALESCET_SPACESHIPT_NOELSET_PLUS_EQUALT_MINUS_EQUALT_MUL_EQUALT_POW_EQUALT_DIV_EQUALT_CONCAT_EQUALT_MOD_EQUALT_AND_EQUALT_OR_EQUALT_XOR_EQUALT_SL_EQUALT_SR_EQUALT_COALESCE_EQUALT_BOOLEAN_ORT_BOOLEAN_ANDT_POWT_SLT_SRT_IS_IDENTICALT_IS_NOT_IDENTICALT_IS_EQUALT_IS_NOT_EQUALT_IS_SMALLER_OR_EQUALT_IS_GREATER_OR_EQUALT_NULLSAFE_OBJECT_OPERATORT_MATCHT_ATTRIBUTET_NAME_RELATIVET_NAME_QUALIFIEDT_NAME_FULLY_QUALIFIEDT_READONLYT_ENUM"
|
||||
const _ID_name = "T_INCLUDET_INCLUDE_ONCET_EXITT_IFT_LNUMBERT_DNUMBERT_STRINGT_STRING_VARNAMET_VARIABLET_NUM_STRINGT_INLINE_HTMLT_CHARACTERT_BAD_CHARACTERT_ENCAPSED_AND_WHITESPACET_CONSTANT_ENCAPSED_STRINGT_ECHOT_DOT_WHILET_ENDWHILET_FORT_ENDFORT_FOREACHT_ENDFOREACHT_DECLARET_ENDDECLARET_AST_SWITCHT_ENDSWITCHT_CASET_DEFAULTT_BREAKT_CONTINUET_GOTOT_FUNCTIONT_FNT_CONSTT_RETURNT_TRYT_CATCHT_FINALLYT_THROWT_USET_INSTEADOFT_GLOBALT_VART_UNSETT_ISSETT_EMPTYT_HALT_COMPILERT_CLASST_TRAITT_INTERFACET_EXTENDST_IMPLEMENTST_OBJECT_OPERATORT_DOUBLE_ARROWT_LISTT_ARRAYT_CALLABLET_CLASS_CT_TRAIT_CT_METHOD_CT_FUNC_CT_LINET_FILET_COMMENTT_DOC_COMMENTT_OPEN_TAGT_OPEN_TAG_WITH_ECHOT_CLOSE_TAGT_WHITESPACET_START_HEREDOCT_END_HEREDOCT_DOLLAR_OPEN_CURLY_BRACEST_CURLY_OPENT_PAAMAYIM_NEKUDOTAYIMT_NAMESPACET_NS_CT_DIRT_NS_SEPARATORT_ELLIPSIST_EVALT_REQUIRET_REQUIRE_ONCET_LOGICAL_ORT_LOGICAL_XORT_LOGICAL_ANDT_INSTANCEOFT_NEWT_CLONET_ELSEIFT_ELSET_ENDIFT_PRINTT_YIELDT_STATICT_ABSTRACTT_FINALT_PRIVATET_PROTECTEDT_PUBLICT_INCT_DECT_YIELD_FROMT_INT_CASTT_DOUBLE_CASTT_STRING_CASTT_ARRAY_CASTT_OBJECT_CASTT_BOOL_CASTT_UNSET_CASTT_COALESCET_SPACESHIPT_NOELSET_PLUS_EQUALT_MINUS_EQUALT_MUL_EQUALT_POW_EQUALT_DIV_EQUALT_CONCAT_EQUALT_MOD_EQUALT_AND_EQUALT_OR_EQUALT_XOR_EQUALT_SL_EQUALT_SR_EQUALT_COALESCE_EQUALT_BOOLEAN_ORT_BOOLEAN_ANDT_POWT_SLT_SRT_IS_IDENTICALT_IS_NOT_IDENTICALT_IS_EQUALT_IS_NOT_EQUALT_IS_SMALLER_OR_EQUALT_IS_GREATER_OR_EQUALT_NULLSAFE_OBJECT_OPERATORT_MATCHT_ATTRIBUTET_NAME_RELATIVET_NAME_QUALIFIEDT_NAME_FULLY_QUALIFIEDT_READONLYT_ENUMT_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARGT_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG"
|
||||
|
||||
var _ID_index = [...]uint16{0, 9, 23, 29, 33, 42, 51, 59, 75, 85, 97, 110, 121, 136, 161, 187, 193, 197, 204, 214, 219, 227, 236, 248, 257, 269, 273, 281, 292, 298, 307, 314, 324, 330, 340, 344, 351, 359, 364, 371, 380, 387, 392, 403, 411, 416, 423, 430, 437, 452, 459, 466, 477, 486, 498, 515, 529, 535, 542, 552, 561, 570, 580, 588, 594, 600, 609, 622, 632, 652, 663, 675, 690, 703, 729, 741, 763, 774, 780, 785, 799, 809, 815, 824, 838, 850, 863, 876, 888, 893, 900, 908, 914, 921, 928, 935, 943, 953, 960, 969, 980, 988, 993, 998, 1010, 1020, 1033, 1046, 1058, 1071, 1082, 1094, 1104, 1115, 1123, 1135, 1148, 1159, 1170, 1181, 1195, 1206, 1217, 1227, 1238, 1248, 1258, 1274, 1286, 1299, 1304, 1308, 1312, 1326, 1344, 1354, 1368, 1389, 1410, 1436, 1443, 1454, 1469, 1485, 1507, 1517, 1523}
|
||||
var _ID_index = [...]uint16{0, 9, 23, 29, 33, 42, 51, 59, 75, 85, 97, 110, 121, 136, 161, 187, 193, 197, 204, 214, 219, 227, 236, 248, 257, 269, 273, 281, 292, 298, 307, 314, 324, 330, 340, 344, 351, 359, 364, 371, 380, 387, 392, 403, 411, 416, 423, 430, 437, 452, 459, 466, 477, 486, 498, 515, 529, 535, 542, 552, 561, 570, 580, 588, 594, 600, 609, 622, 632, 652, 663, 675, 690, 703, 729, 741, 763, 774, 780, 785, 799, 809, 815, 824, 838, 850, 863, 876, 888, 893, 900, 908, 914, 921, 928, 935, 943, 953, 960, 969, 980, 988, 993, 998, 1010, 1020, 1033, 1046, 1058, 1071, 1082, 1094, 1104, 1115, 1123, 1135, 1148, 1159, 1170, 1181, 1195, 1206, 1217, 1227, 1238, 1248, 1258, 1274, 1286, 1299, 1304, 1308, 1312, 1326, 1344, 1354, 1368, 1389, 1410, 1436, 1443, 1454, 1469, 1485, 1507, 1517, 1523, 1560, 1601}
|
||||
|
||||
func (i ID) String() string {
|
||||
i -= 57346
|
||||
|
@ -261,6 +261,17 @@ func (v *Dumper) Union(n *ast.Union) {
|
||||
v.print(v.indent, "},\n")
|
||||
}
|
||||
|
||||
func (v *Dumper) Intersection(n *ast.Intersection) {
|
||||
v.print(0, "&ast.Intersection{\n")
|
||||
v.indent++
|
||||
|
||||
v.dumpPosition(n.Position)
|
||||
v.dumpVertexList("Types", n.Types)
|
||||
v.dumpTokenList("SeparatorTkns", n.SeparatorTkns)
|
||||
|
||||
v.indent--
|
||||
v.print(v.indent, "},\n")
|
||||
}
|
||||
func (v *Dumper) Attribute(n *ast.Attribute) {
|
||||
v.print(0, "&ast.Attribute{\n")
|
||||
v.indent++
|
||||
|
@ -237,6 +237,12 @@ func (f *formatter) Union(n *ast.Union) {
|
||||
}
|
||||
}
|
||||
|
||||
func (f *formatter) Intersection(n *ast.Intersection) {
|
||||
if len(n.Types) > 0 {
|
||||
n.SeparatorTkns = f.formatList(n.Types, '&')
|
||||
}
|
||||
}
|
||||
|
||||
func (f *formatter) Attribute(n *ast.Attribute) {
|
||||
n.Name.Accept(f)
|
||||
n.OpenParenthesisTkn = f.newToken('(', []byte("("))
|
||||
|
@ -50,6 +50,10 @@ func (v *Null) Union(_ *ast.Union) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
func (v *Null) Intersection(_ *ast.Intersection) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
func (v *Null) Attribute(_ *ast.Attribute) {
|
||||
// do nothing
|
||||
}
|
||||
|
@ -180,6 +180,10 @@ func (p *printer) Union(n *ast.Union) {
|
||||
p.printSeparatedList(n.Types, n.SeparatorTkns, []byte("|"))
|
||||
}
|
||||
|
||||
func (p *printer) Intersection(n *ast.Intersection) {
|
||||
p.printSeparatedList(n.Types, n.SeparatorTkns, []byte("&"))
|
||||
}
|
||||
|
||||
func (p *printer) Attribute(n *ast.Attribute) {
|
||||
p.printNode(n.Name)
|
||||
p.printToken(n.OpenParenthesisTkn, p.ifNodeList(n.Args, []byte("(")))
|
||||
|
@ -104,3 +104,13 @@ $foo = $closure->__invoke(...);
|
||||
// new Foo(...); // not working
|
||||
`)
|
||||
}
|
||||
|
||||
func TestIntersectionTypesSyntaxPHP81(t *testing.T) {
|
||||
tester.NewParserPrintTestSuite(t).UsePHP8().Run(`<?php
|
||||
class Test {
|
||||
public A&B $prop;
|
||||
}
|
||||
|
||||
function test(A&B $a): A&B {}
|
||||
`)
|
||||
}
|
||||
|
@ -78,6 +78,14 @@ func (t *Traverser) Union(n *ast.Union) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Traverser) Intersection(n *ast.Intersection) {
|
||||
n.Accept(t.v)
|
||||
|
||||
for _, nn := range n.Types {
|
||||
nn.Accept(t)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Traverser) Attribute(n *ast.Attribute) {
|
||||
n.Accept(t.v)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user