php8.1: added intersection types support (#29)

This commit is contained in:
Makhnev Petr
2022-06-26 03:31:29 +03:00
committed by GitHub
parent 7f6cd25376
commit e16671724e
21 changed files with 22940 additions and 22127 deletions

View File

@@ -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,

View File

@@ -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

View File

@@ -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()
}

View File

@@ -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{
{

View File

@@ -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

View File

@@ -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),

File diff suppressed because it is too large Load Diff

View File

@@ -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; };

View File

@@ -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()
}

View File

@@ -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(),