@ -34,10 +34,14 @@ module.exports = grammar({
externals : $ => [
$ . _automatic _semicolon ,
$ . heredoc ,
$ . encapsed _string _chars ,
$ . encapsed _string _chars _after _variable ,
$ . encapsed _string _chars _heredoc ,
$ . encapsed _string _chars _after _variable _heredoc ,
$ . _eof ,
$ . heredoc _start ,
$ . heredoc _end ,
$ . nowdoc _string ,
$ . sentinel _error , // Unused token used to indicate error recovery mode
] ,
@ -64,6 +68,7 @@ module.exports = grammar({
[ $ . if _statement ] ,
[ $ . namespace _name ] ,
[ $ . heredoc _body ] ,
[ $ . namespace _name _as _prefix ] ,
[ $ . namespace _use _declaration , $ . namespace _name _as _prefix ]
@ -298,6 +303,7 @@ module.exports = grammar({
final _modifier : $ => keyword ( 'final' ) ,
abstract _modifier : $ => keyword ( 'abstract' ) ,
readonly _modifier : $ => keyword ( 'readonly' ) ,
class _interface _clause : $ => seq (
keyword ( 'implements' ) ,
@ -339,7 +345,8 @@ module.exports = grammar({
$ . visibility _modifier ,
$ . static _modifier ,
$ . final _modifier ,
$ . abstract _modifier
$ . abstract _modifier ,
$ . readonly _modifier
) ) ,
property _element : $ => seq (
@ -446,6 +453,7 @@ module.exports = grammar({
property _promotion _parameter : $ => seq (
field ( 'visibility' , $ . visibility _modifier ) ,
field ( 'readonly' , optional ( $ . readonly _modifier ) ) ,
field ( 'type' , optional ( $ . _type ) ) , // Note: callable is not a valid type here, but instead of complicating the parser, we defer this checking to any intelligence using the parser
field ( 'name' , $ . variable _name ) ,
optional ( seq (
@ -491,6 +499,8 @@ module.exports = grammar({
)
) ,
bottom _type : $ => 'never' ,
union _type : $ => prec . right ( pipeSep1 ( $ . _types ) ) ,
primitive _type : $ => choice (
@ -523,7 +533,7 @@ module.exports = grammar({
keyword ( 'unset' , false )
) ,
_return _type : $ => seq ( ':' , field ( 'return_type' , $. _type ) ) ,
_return _type : $ => seq ( ':' , field ( 'return_type' , choice( $. _type , $ . bottom _type ) ) ) ,
const _element : $ => seq (
choice ( $ . name , alias ( $ . _reserved _identifier , $ . name ) ) , '=' , $ . _expression
@ -845,7 +855,7 @@ module.exports = grammar({
) ,
unary _op _expression : $ => choice (
seq( '@' , $ . _expression ) ,
prec( PREC . INC , seq( '@' , $ . _expression ) ) ,
prec . left ( PREC . NEG , seq ( choice ( '+' , '-' , '~' , '!' ) , $ . _expression ) )
) ,
@ -1097,17 +1107,24 @@ module.exports = grammar({
keyword ( 'static' )
) ) ,
variadic _placeholder : $ => token ( '...' ) ,
arguments : $ => seq (
'(' ,
commaSep ( $ . argument ) ,
optional ( ',' ) ,
')'
choice (
seq (
commaSep ( $ . argument ) ,
optional ( ',' ) ,
) ,
$ . variadic _placeholder ,
) ,
')' ,
) ,
argument : $ => seq (
optional ( seq ( field ( 'name' , $ . name ) , ':' ) ) ,
optional ( field ( 'reference_modifier' , $ . reference _modifier ) ) ,
choice ( $. variadic _unpacking , $ . _expression )
choice ( alias( $ . _reserved _identifier , $ . name ) , $. variadic _unpacking , $ . _expression )
) ,
member _call _expression : $ => prec ( PREC . CALL , seq (
@ -1163,11 +1180,14 @@ module.exports = grammar({
seq ( '[' , commaSep ( $ . array _element _initializer ) , optional ( ',' ) , ']' )
) ,
attribute _ list: $ => repeat1 ( seq (
attribute _ group: $ => seq (
'#[' ,
commaSep1 ( $ . attribute ) ,
optional ( ',' ) ,
']' ,
) ) ,
) ,
attribute _list : $ => repeat1 ( $ . attribute _group ) ,
attribute : $ => seq (
choice ( $ . name , alias ( $ . _reserved _identifier , $ . name ) , $ . qualified _name ) ,
@ -1226,22 +1246,38 @@ module.exports = grammar({
)
) ) ,
_interpolated _string _body : $ => repeat1 (
choice (
$ . escape _sequence ,
seq ( $ . variable _name , alias ( $ . encapsed _string _chars _after _variable , $ . string _value ) ) ,
alias ( $ . encapsed _string _chars , $ . string _value ) ,
$ . _simple _string _part ,
$ . _complex _string _part ,
alias ( '\\u' , $ . string _value ) ,
alias ( "'" , $ . string _value ) // Needed to avoid the edge case "$b'" from breaking parsing by trying to apply the $.string rule for some reason
) ,
) ,
_interpolated _string _body _heredoc : $ => repeat1 (
choice (
$ . escape _sequence ,
seq ( $ . variable _name , alias ( $ . encapsed _string _chars _after _variable _heredoc , $ . string _value ) ) ,
alias ( $ . encapsed _string _chars _heredoc , $ . string _value ) ,
$ . _simple _string _part ,
$ . _complex _string _part ,
alias ( '\\u' , $ . string _value ) ,
alias ( "'" , $ . string _value ) , // Needed to avoid the edge case "$b'" from breaking parsing by trying to apply the $.string rule for some reason
alias ( '<?' , $ . string _value ) ,
alias ( token ( prec ( 1 , '?>' ) ) , $ . string _value ) ,
) ,
) ,
encapsed _string : $ => prec . right ( seq (
choice (
/[bB]"/ ,
'"' ,
) ,
repeat (
choice (
$ . escape _sequence ,
seq ( $ . variable _name , alias ( $ . encapsed _string _chars _after _variable , $ . string ) ) ,
alias ( $ . encapsed _string _chars , $ . string ) ,
$ . _simple _string _part ,
$ . _complex _string _part ,
alias ( '\\u' , $ . string ) ,
alias ( "'" , $ . string ) // Needed to avoid the edge case "$b'" from breaking parsing by trying to apply the $.string rule for some reason
) ,
) ,
optional ( $ . _interpolated _string _body ) ,
'"' ,
) ) ,
@ -1250,15 +1286,71 @@ module.exports = grammar({
/[bB]'/ ,
"'"
) ,
token( prec ( 1 , repeat ( /\\'|\\\\|\\?[^'\\]/ ) ) ) , // prec(1, ...) is needed to avoid conflict with $.comment
$. string _value ,
"'" ,
) ,
string _value : $ => token ( prec ( 1 , repeat ( /\\'|\\\\|\\?[^'\\]/ ) ) ) , // prec(1, ...) is needed to avoid conflict with $.comment
heredoc _body : $ => seq ( $ . _new _line ,
repeat1 ( prec . right (
seq ( optional ( $ . _new _line ) , $ . _interpolated _string _body _heredoc ) ,
) ) ,
) ,
heredoc : $ => seq (
token ( '<<<' ) ,
field ( 'identifier' , choice (
$ . heredoc _start ,
seq ( '"' , $ . heredoc _start , token . immediate ( '"' ) )
) ) ,
choice (
seq (
field ( 'value' , $ . heredoc _body ) ,
$ . _new _line ,
field ( 'end_tag' , $ . heredoc _end ) ,
) ,
seq (
field ( 'value' , optional ( $ . heredoc _body ) ) ,
field ( 'end_tag' , $ . heredoc _end ) ,
)
) ,
) ,
_new _line : $ => choice ( token ( /\r?\n/ ) , token ( /\r/ ) ) ,
nowdoc _body : $ => seq ( $ . _new _line ,
choice (
repeat1 (
$ . nowdoc _string
) ,
alias ( "" , $ . nowdoc _string )
)
) ,
nowdoc : $ => seq (
token ( '<<<' ) ,
"'" ,
field ( 'identifier' , $ . heredoc _start ) ,
token . immediate ( "'" ) ,
choice (
seq (
field ( 'value' , $ . nowdoc _body ) ,
$ . _new _line ,
field ( 'end_tag' , $ . heredoc _end ) ,
) ,
seq (
field ( 'value' , optional ( $ . nowdoc _body ) ) ,
field ( 'end_tag' , $ . heredoc _end ) ,
)
) ,
) ,
boolean : $ => / [ Tt ] [ Rr ] [ Uu ] [ Ee ] | [ Ff ] [ Aa ] [ Ll ] [ Ss ] [ Ee ] / ,
null : $ => keyword ( 'null' , false ) ,
_string : $ => choice ( $ . encapsed _string , $ . string , $ . heredoc ) ,
_string : $ => choice ( $ . encapsed _string , $ . string , $ . heredoc , $ . nowdoc ),
dynamic _variable _name : $ => choice (
seq ( '$' , $ . _variable _name ) ,
@ -1299,7 +1391,11 @@ module.exports = grammar({
field ( 'operator' , keyword ( 'instanceof' ) ) ,
field ( 'right' , $ . _class _type _designator )
) ) ,
prec . right ( PREC . NULL _COALESCE , seq ( $ . _expression , '??' , $ . _expression ) ) ,
prec . right ( PREC . NULL _COALESCE , seq (
field ( 'left' , $ . _expression ) ,
field ( 'operator' , '??' ) ,
field ( 'right' , $ . _expression )
) ) ,
... [
[ keyword ( 'and' ) , PREC . LOGICAL _AND _2 ] ,
[ keyword ( 'or' ) , PREC . LOGICAL _OR _2 ] ,