php8.1: added intersection types support (#29)
This commit is contained in:
@@ -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_arg_ref:
|
||||
optional_ref:
|
||||
/* empty */ { $$ = nil }
|
||||
| '&' { $$ = $1 }
|
||||
| ampersand { $$ = $1 }
|
||||
;
|
||||
|
||||
optional_arg_ref:
|
||||
/* empty */ { $$ = nil }
|
||||
| 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) }
|
||||
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,7 @@ 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),
|
||||
@@ -2119,6 +2149,15 @@ expr_without_variable:
|
||||
Right: $3,
|
||||
}
|
||||
}
|
||||
| expr T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG expr
|
||||
{
|
||||
$$ = &ast.ExprBinaryBitwiseAnd{
|
||||
Position: yylex.(*Parser).builder.Pos.NewNodesPosition($1, $3),
|
||||
Left: $1,
|
||||
OpTkn: $2,
|
||||
Right: $3,
|
||||
}
|
||||
}
|
||||
| expr '^' expr
|
||||
{
|
||||
$$ = &ast.ExprBinaryBitwiseXor{
|
||||
@@ -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),
|
||||
|
||||
38509
internal/php8/scanner.go
38509
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(),
|
||||
|
||||
Reference in New Issue
Block a user