From 3697f3956ddd8e35bd10ae81e0abf0ce6d479999 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 3 Dec 2021 17:03:46 +0100 Subject: [PATCH] Use operator keyword instead of magic methods --- .../operator_overloads/add_operator.phpt | 2 +- .../operator_overloads/div_operator.phpt | 2 +- .../equals_fallback_comparison.phpt | 2 +- .../operator_overloads/equals_operator.phpt | 2 +- .../operator_overloads/mod_operator.phpt | 2 +- .../operator_overloads/mul_operator.phpt | 2 +- .../operator_omitted_type.phpt | 4 +- .../operator_overloaded.phpt | 94 +++++++-------- .../operator_unimplemented.phpt | 2 +- .../operator_overloads/pow_operator.phpt | 2 +- .../operator_overloads/sub_operator.phpt | 2 +- Zend/zend_API.c | 112 +++++++++--------- Zend/zend_compile.h | 28 ++--- Zend/zend_language_parser.y | 43 +++++-- Zend/zend_language_scanner.l | 27 +++-- ext/tokenizer/tokenizer_data.c | 2 + 16 files changed, 181 insertions(+), 147 deletions(-) diff --git a/Zend/tests/operator_overloads/add_operator.phpt b/Zend/tests/operator_overloads/add_operator.phpt index deff6df64ee4f..5aa63e524c683 100644 --- a/Zend/tests/operator_overloads/add_operator.phpt +++ b/Zend/tests/operator_overloads/add_operator.phpt @@ -6,7 +6,7 @@ operator overload: add operator with scalars class A { public int $value; - public function __add(int $other, bool $left): A + public operator +(int $other, bool $left): A { $return = new A(); $return->value = $this->value + $other; diff --git a/Zend/tests/operator_overloads/div_operator.phpt b/Zend/tests/operator_overloads/div_operator.phpt index f15fb3e3b5d8b..558dd8a098171 100644 --- a/Zend/tests/operator_overloads/div_operator.phpt +++ b/Zend/tests/operator_overloads/div_operator.phpt @@ -6,7 +6,7 @@ operator overload: div operator with scalars class A { public int $value; - public function __div(int $other, bool $left): A + public operator /(int $other, bool $left): A { $return = new A(); diff --git a/Zend/tests/operator_overloads/equals_fallback_comparison.phpt b/Zend/tests/operator_overloads/equals_fallback_comparison.phpt index 025cc4105af2b..57e196e77a596 100644 --- a/Zend/tests/operator_overloads/equals_fallback_comparison.phpt +++ b/Zend/tests/operator_overloads/equals_fallback_comparison.phpt @@ -6,7 +6,7 @@ operator overload: equals fallback to comparison operator class A { public int $value; - public function __compareto(mixed $other): int + public operator <=>(mixed $other): int { return ($this->value <=> $other); } diff --git a/Zend/tests/operator_overloads/equals_operator.phpt b/Zend/tests/operator_overloads/equals_operator.phpt index ac48c6cc0f1c5..7357b5c0f40d3 100644 --- a/Zend/tests/operator_overloads/equals_operator.phpt +++ b/Zend/tests/operator_overloads/equals_operator.phpt @@ -6,7 +6,7 @@ operator overload: equals operator class A { public int $value; - public function __equals(mixed $other): bool + public operator ==(mixed $other): bool { return ($this->value == $other); } diff --git a/Zend/tests/operator_overloads/mod_operator.phpt b/Zend/tests/operator_overloads/mod_operator.phpt index 41414c090ca21..5f80cc674b929 100644 --- a/Zend/tests/operator_overloads/mod_operator.phpt +++ b/Zend/tests/operator_overloads/mod_operator.phpt @@ -6,7 +6,7 @@ operator overload: mod operator with scalars class A { public int $value; - public function __mod(int $other, bool $left): A + public operator %(int $other, bool $left): A { $return = new A(); diff --git a/Zend/tests/operator_overloads/mul_operator.phpt b/Zend/tests/operator_overloads/mul_operator.phpt index 4eb779a377d9e..07278a88ece42 100644 --- a/Zend/tests/operator_overloads/mul_operator.phpt +++ b/Zend/tests/operator_overloads/mul_operator.phpt @@ -6,7 +6,7 @@ operator overload: mul operator with scalars class A { public int $value; - public function __mul(int $other, bool $left): A + public operator *(int $other, bool $left): A { $return = new A(); $return->value = $this->value * $other; diff --git a/Zend/tests/operator_overloads/operator_omitted_type.phpt b/Zend/tests/operator_overloads/operator_omitted_type.phpt index 30d74baf32ad1..a73395810f721 100644 --- a/Zend/tests/operator_overloads/operator_omitted_type.phpt +++ b/Zend/tests/operator_overloads/operator_omitted_type.phpt @@ -6,7 +6,7 @@ operator overload: no explicit type class A { public int $value; - public function __add($other, bool $left): self + public operator +($other, bool $left): self { $return = new A(); $return->value = $this->value + $other; @@ -16,4 +16,4 @@ class A { } ?> --EXPECTF-- -Fatal error: A::__add(): Parameter #1 ($other) must explicitly define a type in %s on line %d +Fatal error: A::+(): Parameter #1 ($other) must explicitly define a type in %s on line %d diff --git a/Zend/tests/operator_overloads/operator_overloaded.phpt b/Zend/tests/operator_overloads/operator_overloaded.phpt index 01c37f70a858a..e04c50b4886ba 100644 --- a/Zend/tests/operator_overloads/operator_overloaded.phpt +++ b/Zend/tests/operator_overloads/operator_overloaded.phpt @@ -4,64 +4,64 @@ operator overload: overload called >(mixed $other, bool $left): self { - echo "__bitwiseShiftRight() called\n"; + echo ">>() called\n"; return $this; } } @@ -94,26 +94,26 @@ $obj >> 1; ?> --EXPECT-- -__add() called -__add() called -__sub() called -__sub() called -__mul() called -__mul() called -__div() called -__div() called -__mod() called -__mod() called -__pow() called -__pow() called -__bitwiseAnd() called -__bitwiseAnd() called -__bitwiseOr() called -__bitwiseOr() called -__bitwiseXor() called -__bitwiseXor() called -__bitwiseShiftLeft() called -__bitwiseShiftLeft() called -__bitwiseShiftRight() called -__bitwiseShiftRight() called -__bitwiseNot() called ++() called ++() called +-() called +-() called +*() called +*() called +/() called +/() called +%() called +%() called +**() called +**() called +&() called +&() called +|() called +|() called +^() called +^() called +<<() called +<<() called +>>() called +>>() called +~() called diff --git a/Zend/tests/operator_overloads/operator_unimplemented.phpt b/Zend/tests/operator_overloads/operator_unimplemented.phpt index 59f1928d2b1d1..75784449b3c2a 100644 --- a/Zend/tests/operator_overloads/operator_unimplemented.phpt +++ b/Zend/tests/operator_overloads/operator_unimplemented.phpt @@ -10,7 +10,7 @@ class A { class B { public int $value; - public function __add(int|A $other, bool $left): B + public operator +(int|A $other, bool $left): B { $return = new B(); if (is_int($other)) { diff --git a/Zend/tests/operator_overloads/pow_operator.phpt b/Zend/tests/operator_overloads/pow_operator.phpt index 4a7914b2a8be9..a2f56dda411b5 100644 --- a/Zend/tests/operator_overloads/pow_operator.phpt +++ b/Zend/tests/operator_overloads/pow_operator.phpt @@ -6,7 +6,7 @@ operator overload: pow operator with scalars class A { public int $value; - public function __pow(int $other, bool $left): A + public operator **(int $other, bool $left): A { $return = new A(); diff --git a/Zend/tests/operator_overloads/sub_operator.phpt b/Zend/tests/operator_overloads/sub_operator.phpt index 88910f249c4c5..a45c79ca6d32f 100644 --- a/Zend/tests/operator_overloads/sub_operator.phpt +++ b/Zend/tests/operator_overloads/sub_operator.phpt @@ -6,7 +6,7 @@ operator overload: sub operator with scalars class A { public int $value; - public function __sub(int $other, bool $left): A + public operator -(int $other, bool $left): A { $return = new A(); diff --git a/Zend/zend_API.c b/Zend/zend_API.c index ec13656529cef..2a1c3b3f0fff0 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2551,6 +2551,41 @@ static void zend_check_magic_method_equality_operator_overload( ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce, const zend_function *fptr, zend_string *lcname, int error_type) /* {{{ */ { + if (zend_string_equals_literal(lcname, ZEND_ADD_FUNC_NAME)) { + zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + } else if (zend_string_equals_literal(lcname, ZEND_SUB_FUNC_NAME)) { + zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + } else if (zend_string_equals_literal(lcname, ZEND_MUL_FUNC_NAME)) { + zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + } else if (zend_string_equals_literal(lcname, ZEND_DIV_FUNC_NAME)) { + zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + } else if (zend_string_equals_literal(lcname, ZEND_MOD_FUNC_NAME)) { + zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + } else if (zend_string_equals_literal(lcname, ZEND_POW_FUNC_NAME)) { + zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + } else if (zend_string_equals_literal(lcname, ZEND_BWAND_FUNC_NAME)) { + zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + } else if (zend_string_equals_literal(lcname, ZEND_BWOR_FUNC_NAME)) { + zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + } else if (zend_string_equals_literal(lcname, ZEND_BWXOR_FUNC_NAME)) { + zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + } else if (zend_string_equals_literal(lcname, ZEND_BWSL_FUNC_NAME)) { + zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + } else if (zend_string_equals_literal(lcname, ZEND_BWSR_FUNC_NAME)) { + zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + } else if (zend_string_equals_literal(lcname, ZEND_BWNOT_FUNC_NAME)) { + zend_check_magic_method_unary_operator_overload(ce, fptr, error_type); + } else if (zend_string_equals_literal(lcname, ZEND_EQ_FUNC_NAME)) { + zend_check_magic_method_equality_operator_overload(ce, fptr, error_type); + } else if (zend_string_equals_literal(lcname, ZEND_COMPARE_FUNC_NAME)) { + zend_check_magic_method_args(1, ce, fptr, error_type); + zend_check_magic_method_non_static(ce, fptr, error_type); + zend_check_magic_method_public(ce, fptr, error_type); + zend_check_magic_method_explicit_type(0, ce, fptr, error_type); + zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_ANY); + zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_LONG); + } + if (ZSTR_VAL(fptr->common.function_name)[0] != '_' || ZSTR_VAL(fptr->common.function_name)[1] != '_') { return; @@ -2642,45 +2677,42 @@ ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce, zend_check_magic_method_non_static(ce, fptr, error_type); zend_check_magic_method_public(ce, fptr, error_type); zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_VOID); - } else if (zend_string_equals_literal(lcname, ZEND_ADD_FUNC_NAME)) { - zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + } +} +/* }}} */ + +ZEND_API void zend_add_magic_method(zend_class_entry *ce, zend_function *fptr, zend_string *lcname) +{ + if (zend_string_equals_literal(lcname, ZEND_ADD_FUNC_NAME)) { + ce->__add = fptr; } else if (zend_string_equals_literal(lcname, ZEND_SUB_FUNC_NAME)) { - zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + ce->__sub = fptr; } else if (zend_string_equals_literal(lcname, ZEND_MUL_FUNC_NAME)) { - zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + ce->__mul = fptr; } else if (zend_string_equals_literal(lcname, ZEND_DIV_FUNC_NAME)) { - zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + ce->__div = fptr; } else if (zend_string_equals_literal(lcname, ZEND_MOD_FUNC_NAME)) { - zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + ce->__mod = fptr; } else if (zend_string_equals_literal(lcname, ZEND_POW_FUNC_NAME)) { - zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + ce->__pow = fptr; } else if (zend_string_equals_literal(lcname, ZEND_BWAND_FUNC_NAME)) { - zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + ce->__bitwiseand = fptr; } else if (zend_string_equals_literal(lcname, ZEND_BWOR_FUNC_NAME)) { - zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + ce->__bitwiseor = fptr; } else if (zend_string_equals_literal(lcname, ZEND_BWXOR_FUNC_NAME)) { - zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + ce->__bitwisexor = fptr; + } else if (zend_string_equals_literal(lcname, ZEND_BWNOT_FUNC_NAME)) { + ce->__bitwisenot = fptr; } else if (zend_string_equals_literal(lcname, ZEND_BWSL_FUNC_NAME)) { - zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); + ce->__bitwiseshiftleft = fptr; } else if (zend_string_equals_literal(lcname, ZEND_BWSR_FUNC_NAME)) { - zend_check_magic_method_binary_operator_overload(ce, fptr, error_type); - } else if (zend_string_equals_literal(lcname, ZEND_BWNOT_FUNC_NAME)) { - zend_check_magic_method_unary_operator_overload(ce, fptr, error_type); + ce->__bitwiseshiftright = fptr; } else if (zend_string_equals_literal(lcname, ZEND_EQ_FUNC_NAME)) { - zend_check_magic_method_equality_operator_overload(ce, fptr, error_type); + ce->__equals = fptr; } else if (zend_string_equals_literal(lcname, ZEND_COMPARE_FUNC_NAME)) { - zend_check_magic_method_args(1, ce, fptr, error_type); - zend_check_magic_method_non_static(ce, fptr, error_type); - zend_check_magic_method_public(ce, fptr, error_type); - zend_check_magic_method_explicit_type(0, ce, fptr, error_type); - zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_ANY); - zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_LONG); + ce->__compareto = fptr; } -} -/* }}} */ - -ZEND_API void zend_add_magic_method(zend_class_entry *ce, zend_function *fptr, zend_string *lcname) -{ + if (ZSTR_VAL(lcname)[0] != '_' || ZSTR_VAL(lcname)[1] != '_') { /* pass */ } else if (zend_string_equals_literal(lcname, ZEND_CLONE_FUNC_NAME)) { @@ -2714,34 +2746,6 @@ ZEND_API void zend_add_magic_method(zend_class_entry *ce, zend_function *fptr, z ce->__serialize = fptr; } else if (zend_string_equals_literal(lcname, "__unserialize")) { ce->__unserialize = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_ADD_FUNC_NAME)) { - ce->__add = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_SUB_FUNC_NAME)) { - ce->__sub = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_MUL_FUNC_NAME)) { - ce->__mul = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_DIV_FUNC_NAME)) { - ce->__div = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_MOD_FUNC_NAME)) { - ce->__mod = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_POW_FUNC_NAME)) { - ce->__pow = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_BWAND_FUNC_NAME)) { - ce->__bitwiseand = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_BWOR_FUNC_NAME)) { - ce->__bitwiseor = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_BWXOR_FUNC_NAME)) { - ce->__bitwisexor = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_BWNOT_FUNC_NAME)) { - ce->__bitwisenot = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_BWSL_FUNC_NAME)) { - ce->__bitwiseshiftleft = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_BWSR_FUNC_NAME)) { - ce->__bitwiseshiftright = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_EQ_FUNC_NAME)) { - ce->__equals = fptr; - } else if (zend_string_equals_literal(lcname, ZEND_COMPARE_FUNC_NAME)) { - ce->__compareto = fptr; } } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index aab5d63bd528b..f13a81e6a1054 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -1108,22 +1108,22 @@ END_EXTERN_C() /* operator overload functions */ /* binary operators */ -#define ZEND_ADD_FUNC_NAME "__add" -#define ZEND_SUB_FUNC_NAME "__sub" -#define ZEND_MUL_FUNC_NAME "__mul" -#define ZEND_DIV_FUNC_NAME "__div" -#define ZEND_MOD_FUNC_NAME "__mod" -#define ZEND_POW_FUNC_NAME "__pow" -#define ZEND_BWAND_FUNC_NAME "__bitwiseand" -#define ZEND_BWOR_FUNC_NAME "__bitwiseor" -#define ZEND_BWXOR_FUNC_NAME "__bitwisexor" -#define ZEND_BWSL_FUNC_NAME "__bitwiseshiftleft" -#define ZEND_BWSR_FUNC_NAME "__bitwiseshiftright" +#define ZEND_ADD_FUNC_NAME "+" +#define ZEND_SUB_FUNC_NAME "-" +#define ZEND_MUL_FUNC_NAME "*" +#define ZEND_DIV_FUNC_NAME "/" +#define ZEND_MOD_FUNC_NAME "%" +#define ZEND_POW_FUNC_NAME "**" +#define ZEND_BWAND_FUNC_NAME "&" +#define ZEND_BWOR_FUNC_NAME "|" +#define ZEND_BWXOR_FUNC_NAME "^" +#define ZEND_BWSL_FUNC_NAME "<<" +#define ZEND_BWSR_FUNC_NAME ">>" /* unary operators */ -#define ZEND_BWNOT_FUNC_NAME "__bitwisenot" +#define ZEND_BWNOT_FUNC_NAME "~" /* comparison operators */ -#define ZEND_EQ_FUNC_NAME "__equals" -#define ZEND_COMPARE_FUNC_NAME "__compareto" +#define ZEND_EQ_FUNC_NAME "==" +#define ZEND_COMPARE_FUNC_NAME "<=>" /* The following constants may be combined in CG(compiler_options) * to change the default compiler behavior */ diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 65280f3143808..6fd80dd4b0ccc 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -137,6 +137,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_BREAK "'break'" %token T_CONTINUE "'continue'" %token T_GOTO "'goto'" +%token T_OPERATOR "'operator'" %token T_FUNCTION "'function'" %token T_FN "'fn'" %token T_CONST "'const'" @@ -178,6 +179,14 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_METHOD_C "'__METHOD__'" %token T_FUNC_C "'__FUNCTION__'" %token T_NS_C "'__NAMESPACE__'" +%token '+' "'+'" +%token '-' "'-'" +%token '*' "'*'" +%token '/' "'/'" +%token '%' "'%'" +%token '|' "'|'" +%token '^' "'^'" +%token '~' "'~'" %token END 0 "end of file" %token T_ATTRIBUTE "'#['" @@ -195,15 +204,15 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_COALESCE_EQUAL "'??='" %token T_BOOLEAN_OR "'||'" %token T_BOOLEAN_AND "'&&'" -%token T_IS_EQUAL "'=='" +%token T_IS_EQUAL "'=='" %token T_IS_NOT_EQUAL "'!='" %token T_IS_IDENTICAL "'==='" %token T_IS_NOT_IDENTICAL "'!=='" %token T_IS_SMALLER_OR_EQUAL "'<='" %token T_IS_GREATER_OR_EQUAL "'>='" -%token T_SPACESHIP "'<=>'" -%token T_SL "'<<'" -%token T_SR "'>>'" +%token T_SPACESHIP "'<=>'" +%token T_SL "'<<'" +%token T_SR "'>>'" %token T_INC "'++'" %token T_DEC "'--'" %token T_INT_CAST "'(int)'" @@ -230,15 +239,15 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_NS_SEPARATOR "'\\'" %token T_ELLIPSIS "'...'" %token T_COALESCE "'??'" -%token T_POW "'**'" +%token T_POW "'**'" %token T_POW_EQUAL "'**='" /* We need to split the & token in two to avoid a shift/reduce conflict. For T1&$v and T1&T2, * with only one token lookahead, bison does not know whether to reduce T1 as a complete type, * or shift to continue parsing an intersection type. */ -%token T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG "'&'" +%token T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG "'&'" /* Bison warns on duplicate token literals, so use a different dummy value here. * It will be fixed up by zend_yytnamerr() later. */ -%token T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG "amp" +%token T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG "amp" %token T_BAD_CHARACTER "invalid character" /* Token used to force a parse error from the lexer */ @@ -279,7 +288,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type match match_arm_list non_empty_match_arm_list match_arm match_arm_cond_list %type enum_declaration_statement enum_backing_type enum_case enum_case_expr -%type returns_ref function fn is_reference is_variadic variable_modifiers +%type returns_ref operator function fn is_reference is_variadic variable_modifiers %type method_modifiers non_empty_member_modifiers member_modifier %type optional_property_modifiers property_modifier %type class_modifiers class_modifier use_type backup_fn_flags @@ -287,7 +296,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type backup_lex_pos %type backup_doc_comment -%type reserved_non_modifiers semi_reserved +%type reserved_non_modifiers semi_reserved ampersand overloadable_operator_list %% /* Rules */ @@ -302,7 +311,7 @@ reserved_non_modifiers: | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO | T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT | T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS - | T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_FN | T_MATCH | T_ENUM + | T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_FN | T_MATCH | T_ENUM | T_OPERATOR ; semi_reserved: @@ -310,6 +319,10 @@ semi_reserved: | T_STATIC | T_ABSTRACT | T_FINAL | T_PRIVATE | T_PROTECTED | T_PUBLIC | T_READONLY ; +overloadable_operator_list: + '+' | '-' | '*' | '/' | '%' | ampersand | '|' | '^' | '~' | T_SL | T_SR | T_POW | T_IS_EQUAL | T_SPACESHIP +; + ampersand: T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG | T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG @@ -917,6 +930,12 @@ attributed_class_statement: return_type backup_fn_flags method_body backup_fn_flags { $$ = zend_ast_create_decl(ZEND_AST_METHOD, $3 | $1 | $12, $2, $5, zend_ast_get_str($4), $7, NULL, $11, $9, NULL); CG(extra_fn_flags) = $10; } + | method_modifiers operator overloadable_operator_list backup_doc_comment '(' parameter_list ')' + return_type backup_fn_flags method_body backup_fn_flags + { zval zv; + if (zend_lex_tstring(&zv, $3) == FAILURE) { YYABORT; } + $$ = zend_ast_create_decl(ZEND_AST_METHOD, $1 | $11, $2, $4, Z_STR(zv), $6, NULL, $10, $8, NULL); + CG(extra_fn_flags) = $9; } | enum_case { $$ = $1; } ; @@ -1223,6 +1242,10 @@ function: T_FUNCTION { $$ = CG(zend_lineno); } ; +operator: + T_OPERATOR { $$ = CG(zend_lineno); } +; + backup_doc_comment: %empty { $$ = CG(doc_comment); CG(doc_comment) = NULL; } ; diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index a367c2acb82da..d2b2274e7fabe 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -299,13 +299,14 @@ ZEND_API void zend_restore_lexical_state(zend_lex_state *lex_state) ZEND_API zend_result zend_lex_tstring(zval *zv, unsigned char *ident) { unsigned char *end = ident; - while ((*end >= 'a' && *end <= 'z') || (*end >= 'A' && *end <= 'Z') || *end == '_') { + while ((*end >= 'a' && *end <= 'z') || (*end >= 'A' && *end <= 'Z') || *end == '_' || *end == '<' || *end == '*' || *end == '+' || *end == '|' || *end == '&' || *end == '^' || *end == '>' || *end == '/' || *end == '-' || *end == '%' || *end == '=' || *end == '~') { end++; } size_t length = end - ident; - if (length == 0) { - ZEND_ASSERT(ident[0] == '<' && ident[1] == '?' && ident[2] == '='); + ZEND_ASSERT(length > 0); + + if (ident[0] == '<' && ident[1] == '?' && ident[2] == '=') { zend_throw_exception(zend_ce_parse_error, "Cannot use \""operator" { + RETURN_TOKEN_WITH_IDENT(T_OPERATOR); +} + "const" { RETURN_TOKEN_WITH_IDENT(T_CONST); } @@ -1772,7 +1777,7 @@ NEWLINE ("\r"|"\n"|"\r\n") } "==" { - RETURN_TOKEN(T_IS_EQUAL); + RETURN_TOKEN_WITH_IDENT(T_IS_EQUAL); } "!="|"<>" { @@ -1780,7 +1785,7 @@ NEWLINE ("\r"|"\n"|"\r\n") } "<=>" { - RETURN_TOKEN(T_SPACESHIP); + RETURN_TOKEN_WITH_IDENT(T_SPACESHIP); } "<=" { @@ -1804,7 +1809,7 @@ NEWLINE ("\r"|"\n"|"\r\n") } "*\*" { - RETURN_TOKEN(T_POW); + RETURN_TOKEN_WITH_IDENT(T_POW); } "*\*=" { @@ -1868,20 +1873,20 @@ NEWLINE ("\r"|"\n"|"\r\n") } "<<" { - RETURN_TOKEN(T_SL); + RETURN_TOKEN_WITH_IDENT(T_SL); } ">>" { - RETURN_TOKEN(T_SR); + RETURN_TOKEN_WITH_IDENT(T_SR); } "&"[ \t\r\n]*("$"|"...") { yyless(1); - RETURN_TOKEN(T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG); + RETURN_TOKEN_WITH_IDENT(T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG); } "&" { - RETURN_TOKEN(T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG); + RETURN_TOKEN_WITH_IDENT(T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG); } "]"|")" { @@ -1895,7 +1900,7 @@ NEWLINE ("\r"|"\n"|"\r\n") } {TOKENS} { - RETURN_TOKEN(yytext[0]); + RETURN_TOKEN_WITH_IDENT(yytext[0]); } diff --git a/ext/tokenizer/tokenizer_data.c b/ext/tokenizer/tokenizer_data.c index a5adc67308485..a5604c0d99059 100644 --- a/ext/tokenizer/tokenizer_data.c +++ b/ext/tokenizer/tokenizer_data.c @@ -75,6 +75,7 @@ void tokenizer_register_constants(INIT_FUNC_ARGS) { REGISTER_LONG_CONSTANT("T_BREAK", T_BREAK, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_CONTINUE", T_CONTINUE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_GOTO", T_GOTO, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("T_OPERATOR", T_OPERATOR, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_FUNCTION", T_FUNCTION, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_FN", T_FN, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_CONST", T_CONST, CONST_CS | CONST_PERSISTENT); @@ -228,6 +229,7 @@ char *get_token_type_name(int token_type) case T_BREAK: return "T_BREAK"; case T_CONTINUE: return "T_CONTINUE"; case T_GOTO: return "T_GOTO"; + case T_OPERATOR: return "T_OPERATOR"; case T_FUNCTION: return "T_FUNCTION"; case T_FN: return "T_FN"; case T_CONST: return "T_CONST";