From c5a0e66a0060befff38a5a3d77d41bba5dd460f1 Mon Sep 17 00:00:00 2001 From: "luis.carilla" Date: Sun, 30 Mar 2025 11:48:11 +0200 Subject: [PATCH 01/15] wip --- Dockerfile | 7 +++++++ dev.sh | 2 ++ 2 files changed, 9 insertions(+) create mode 100644 Dockerfile create mode 100755 dev.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000..68861f66e6085 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM ubuntu:noble + +RUN apt update && apt install -y pkg-config build-essential autoconf bison re2c \ + libxml2-dev libsqlite3-dev +WORKDIR /app + +CMD while true; do sleep 3600; done diff --git a/dev.sh b/dev.sh new file mode 100755 index 0000000000000..5d4d2bdd4577f --- /dev/null +++ b/dev.sh @@ -0,0 +1,2 @@ +docker build -t php-dev-setup . +docker run --volume .:/app php-dev-setup From ceec3710ac209b3ff6e7ec9b2e6e431fd2bd7563 Mon Sep 17 00:00:00 2001 From: "luis.carilla" Date: Sun, 30 Mar 2025 13:11:50 +0200 Subject: [PATCH 02/15] language parser working? --- Zend/zend_ast.h | 1 + Zend/zend_language_parser.y | 24 ++++++++++++++++++++---- ext/tokenizer/tokenizer_data.c | 2 ++ ext/tokenizer/tokenizer_data.stub.php | 10 ++++++++++ ext/tokenizer/tokenizer_data_arginfo.h | 4 +++- 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 9348c35f6cc07..0eeb932e25368 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -180,6 +180,7 @@ enum _zend_ast_kind { /* 6 child nodes */ ZEND_AST_PARAM = 6 << ZEND_AST_NUM_CHILDREN_SHIFT, + ZEND_AST_GENERIC_TYPE_PARAM }; typedef uint16_t zend_ast_kind; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 9483a83b4e955..882cf580c5af4 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -237,6 +237,11 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_COALESCE "'??'" %token T_POW "'**'" %token T_POW_EQUAL "'**='" + +// Generics +%token T_GENERIC_START "'<'" +%token T_GENERIC_END "'>'" + /* 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. */ @@ -286,6 +291,8 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type function_name non_empty_member_modifiers %type property_hook property_hook_list optional_property_hook_list hooked_property property_hook_body %type optional_parameter_list +%type generic_type_parameters +%type generic_type_parameter %type returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers %type method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers @@ -597,11 +604,20 @@ is_variadic: class_declaration_statement: class_modifiers T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $7, zend_ast_get_str($4), $5, $6, $9, NULL, NULL); } + T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' generic_type_parameters + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $7, zend_ast_get_str($4), $5, $6, $9, $11, NULL); } | T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $6, zend_ast_get_str($3), $4, $5, $8, NULL, NULL); } + T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' generic_type_parameters + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $6, zend_ast_get_str($3), $4, $5, $8, $10, NULL); } +; + +generic_type_parameters: + T_GENERIC_START generic_type_parameter T_GENERIC_END { $$ = $2; } + | %empty { $$ = NULL; } +; + +generic_type_parameter: + T_STRING { $$ = zend_ast_create_decl(ZEND_AST_GENERIC_TYPE_PARAM, 0, 0, NULL, zend_ast_get_str($1), NULL, NULL, NULL, NULL, NULL); } ; class_modifiers: diff --git a/ext/tokenizer/tokenizer_data.c b/ext/tokenizer/tokenizer_data.c index a1e131032bcfb..e8a4d3006aa4f 100644 --- a/ext/tokenizer/tokenizer_data.c +++ b/ext/tokenizer/tokenizer_data.c @@ -173,6 +173,8 @@ char *get_token_type_name(int token_type) case T_COALESCE: return "T_COALESCE"; case T_POW: return "T_POW"; case T_POW_EQUAL: return "T_POW_EQUAL"; + case T_GENERIC_START: return "T_GENERIC_START"; + case T_GENERIC_END: return "T_GENERIC_END"; case T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG: return "T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG"; case T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG: return "T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG"; case T_BAD_CHARACTER: return "T_BAD_CHARACTER"; diff --git a/ext/tokenizer/tokenizer_data.stub.php b/ext/tokenizer/tokenizer_data.stub.php index c1e1fd254dfaa..fe7321e9cd40d 100644 --- a/ext/tokenizer/tokenizer_data.stub.php +++ b/ext/tokenizer/tokenizer_data.stub.php @@ -742,6 +742,16 @@ * @cvalue T_POW_EQUAL */ const T_POW_EQUAL = UNKNOWN; +/** + * @var int + * @cvalue T_GENERIC_START + */ +const T_GENERIC_START = UNKNOWN; +/** + * @var int + * @cvalue T_GENERIC_END + */ +const T_GENERIC_END = UNKNOWN; /** * @var int * @cvalue T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG diff --git a/ext/tokenizer/tokenizer_data_arginfo.h b/ext/tokenizer/tokenizer_data_arginfo.h index 9c488d19f1890..6ac43d4a612b8 100644 --- a/ext/tokenizer/tokenizer_data_arginfo.h +++ b/ext/tokenizer/tokenizer_data_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 19d25d22098f46283b517352cbb302db962b50fd */ + * Stub hash: 69f8ad0852d9f99a17d98192dfc54fd8961d3d0a */ static void register_tokenizer_data_symbols(int module_number) { @@ -151,6 +151,8 @@ static void register_tokenizer_data_symbols(int module_number) REGISTER_LONG_CONSTANT("T_COALESCE", T_COALESCE, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_POW", T_POW, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_POW_EQUAL", T_POW_EQUAL, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("T_GENERIC_START", T_GENERIC_START, CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("T_GENERIC_END", T_GENERIC_END, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG", T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG", T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_BAD_CHARACTER", T_BAD_CHARACTER, CONST_PERSISTENT); From fb10c061fd1d388402aab3d11d4fdc7939e5326f Mon Sep 17 00:00:00 2001 From: "luis.carilla" Date: Sun, 30 Mar 2025 17:05:14 +0200 Subject: [PATCH 03/15] AST working? --- Zend/zend_language_parser.y | 8 ++++---- Zend/zend_language_scanner.l | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 882cf580c5af4..96d55a4c1f3e0 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -604,11 +604,11 @@ is_variadic: class_declaration_statement: class_modifiers T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' generic_type_parameters - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $7, zend_ast_get_str($4), $5, $6, $9, $11, NULL); } + T_STRING generic_type_parameters extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $8, zend_ast_get_str($4), $6, $7, $10, $5, NULL); } | T_CLASS { $$ = CG(zend_lineno); } - T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' generic_type_parameters - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $6, zend_ast_get_str($3), $4, $5, $8, $10, NULL); } + T_STRING generic_type_parameters extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $7, zend_ast_get_str($3), $5, $6, $9, $4, NULL); } ; generic_type_parameters: diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 4c883b81c5f7d..7e87d352e4ef8 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -1537,6 +1537,28 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN_WITH_IDENT(T_PRINT); } +"<"{LABEL}">" { + yyless(1); + yy_push_state(ST_GENERIC); + RETURN_TOKEN(T_GENERIC_START); +} + +{LABEL}">" { + yy_pop_state(); + yyless(yyleng - 1); + RETURN_TOKEN_WITH_STR(T_STRING, 0); +} + +">" { + yy_pop_state(); + RETURN_TOKEN(T_GENERIC_END); +} + +{WHITESPACE} { + HANDLE_NEWLINES(yytext, yyleng); + RETURN_TOKEN(T_WHITESPACE); +} + "class" { RETURN_TOKEN_WITH_IDENT(T_CLASS); } From 5c729ee011fc3d0bb32e6e89c0fa58e6c2a81f66 Mon Sep 17 00:00:00 2001 From: "luis.carilla" Date: Sun, 30 Mar 2025 19:06:52 +0200 Subject: [PATCH 04/15] AST working? --- Dockerfile | 2 +- Zend/zend_ast.h | 2 +- Zend/zend_language_parser.y | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 68861f66e6085..9afeb9e49ddfe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM ubuntu:noble RUN apt update && apt install -y pkg-config build-essential autoconf bison re2c \ - libxml2-dev libsqlite3-dev + libxml2-dev libsqlite3-dev cmake gdb WORKDIR /app CMD while true; do sleep 3600; done diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 0eeb932e25368..77ad0aa141692 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -70,6 +70,7 @@ enum _zend_ast_kind { ZEND_AST_ATTRIBUTE_GROUP, ZEND_AST_MATCH_ARM_LIST, ZEND_AST_MODIFIER_LIST, + ZEND_AST_GENERIC_TYPE_PARAM_LIST, /* 0 child nodes */ ZEND_AST_MAGIC_CONST = 0 << ZEND_AST_NUM_CHILDREN_SHIFT, @@ -180,7 +181,6 @@ enum _zend_ast_kind { /* 6 child nodes */ ZEND_AST_PARAM = 6 << ZEND_AST_NUM_CHILDREN_SHIFT, - ZEND_AST_GENERIC_TYPE_PARAM }; typedef uint16_t zend_ast_kind; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 96d55a4c1f3e0..0866c45752b3f 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -291,8 +291,8 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type function_name non_empty_member_modifiers %type property_hook property_hook_list optional_property_hook_list hooked_property property_hook_body %type optional_parameter_list +%type generic_type_parameter_list %type generic_type_parameters -%type generic_type_parameter %type returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers %type method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers @@ -612,12 +612,13 @@ class_declaration_statement: ; generic_type_parameters: - T_GENERIC_START generic_type_parameter T_GENERIC_END { $$ = $2; } + T_GENERIC_START generic_type_parameter_list possible_comma T_GENERIC_END { $$ = $2; } | %empty { $$ = NULL; } ; -generic_type_parameter: - T_STRING { $$ = zend_ast_create_decl(ZEND_AST_GENERIC_TYPE_PARAM, 0, 0, NULL, zend_ast_get_str($1), NULL, NULL, NULL, NULL, NULL); } +generic_type_parameter_list: + T_STRING { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_TYPE_PARAM_LIST, $1); } + | generic_type_parameter_list ',' T_STRING { $$ = zend_ast_list_add($1, $3); } ; class_modifiers: From d28b7d12d9d19d60d55a1da7108944e24ad184af Mon Sep 17 00:00:00 2001 From: "luis.carilla" Date: Sun, 30 Mar 2025 21:06:26 +0200 Subject: [PATCH 05/15] tokens working? --- Zend/zend_language_scanner.l | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 7e87d352e4ef8..7a25ea7adf5bc 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -1543,12 +1543,14 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN(T_GENERIC_START); } -{LABEL}">" { - yy_pop_state(); - yyless(yyleng - 1); +{LABEL} { RETURN_TOKEN_WITH_STR(T_STRING, 0); } +"," { + RETURN_TOKEN(','); +} + ">" { yy_pop_state(); RETURN_TOKEN(T_GENERIC_END); From 26dd70c115229c459b320f3afb7c9d6c43c28d7d Mon Sep 17 00:00:00 2001 From: "luis.carilla" Date: Sun, 30 Mar 2025 21:26:30 +0200 Subject: [PATCH 06/15] FUCKING HELL --- Zend/zend_compile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 8e4221673c4cf..75047fc029c71 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -9133,7 +9133,7 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) CG(active_class_entry) = ce; - if (decl->child[3]) { + if (decl->child[3] && decl->child[3]->kind != ZEND_AST_GENERIC_TYPE_PARAM_LIST) { zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS, 0); } From 2920fcf19381ce8f5c54f550a5096c3ded0181eb Mon Sep 17 00:00:00 2001 From: "luis.carilla" Date: Mon, 31 Mar 2025 23:43:14 +0200 Subject: [PATCH 07/15] add generic type to _zend_class_entry --- Zend/zend.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Zend/zend.h b/Zend/zend.h index 0cf1faeb653fe..24952e52b6572 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -144,8 +144,16 @@ struct _zend_inheritance_cache_entry { zend_class_entry *traits_and_interfaces[1]; }; +typedef struct _zend_generic_type { + struct _zend_generic_type *elements; + size_t element_count; + char *type; +} zend_generic_type; + struct _zend_class_entry { + // TYPE HERE char type; + zend_generic_type *generic_type; zend_string *name; /* class_entry or string depending on ZEND_ACC_LINKED */ union { From 73f9ca1d4260582868c83558d8dc90fe33851d65 Mon Sep 17 00:00:00 2001 From: "luis.carilla" Date: Tue, 1 Apr 2025 00:51:36 +0200 Subject: [PATCH 08/15] allow generics in non-static types --- Zend/zend_language_parser.y | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 0866c45752b3f..2c4f6a5ea58b7 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -882,10 +882,11 @@ type_expr_without_static: | intersection_type_without_static { $$ = $1; } ; +//TODO add generic type params to AST type_without_static: - T_ARRAY { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); } + T_ARRAY generic_type_parameters { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); } | T_CALLABLE { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_CALLABLE); } - | name { $$ = $1; } + | name generic_type_parameters { $$ = $1; } ; union_type_without_static_element: From 6d526e05153a170b97d5103323f86437100f7164 Mon Sep 17 00:00:00 2001 From: "luis.carilla" Date: Tue, 1 Apr 2025 19:09:00 +0200 Subject: [PATCH 09/15] kleine kochung --- Zend/zend_language_parser.y | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 2c4f6a5ea58b7..2a7110d690f96 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -293,6 +293,8 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type optional_parameter_list %type generic_type_parameter_list %type generic_type_parameters +%type generic_type +%type non_empty_generic_type_parameters %type returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers %type method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers @@ -300,6 +302,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type backup_lex_pos %type backup_doc_comment +%type generic_param_name %type reserved_non_modifiers semi_reserved @@ -617,8 +620,17 @@ generic_type_parameters: ; generic_type_parameter_list: - T_STRING { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_TYPE_PARAM_LIST, $1); } - | generic_type_parameter_list ',' T_STRING { $$ = zend_ast_list_add($1, $3); } + generic_type { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_TYPE_PARAM_LIST, $1); } + | generic_type_parameter_list ',' generic_type { $$ = zend_ast_list_add($1, $3); } +; + +non_empty_generic_type_parameters: + T_GENERIC_START generic_type_parameter_list possible_comma T_GENERIC_END { $$ = $2; } +; + +generic_type: + T_STRING { $$ = zend_ast_create_zval_from_str(zend_ast_get_str($1)); } + | T_STRING non_empty_generic_type_parameters { $$ = zend_ast_create(ZEND_AST_GENERIC_TYPE, zend_ast_create_zval_from_str(zend_ast_get_str($1)), $2); } ; class_modifiers: @@ -1378,6 +1390,10 @@ backup_doc_comment: %empty { $$ = CG(doc_comment); CG(doc_comment) = NULL; } ; +generic_param_name: + %empty { $$ = CG(doc_comment); CG(doc_comment) = NULL; } +; + backup_fn_flags: %prec PREC_ARROW_FUNCTION %empty { $$ = CG(extra_fn_flags); CG(extra_fn_flags) = 0; } ; From fd7161c8be14a3d370a464c7d79341694dd6ca25 Mon Sep 17 00:00:00 2001 From: "luis.carilla" Date: Tue, 1 Apr 2025 21:12:58 +0200 Subject: [PATCH 10/15] wip --- Dockerfile.php | 8 ++++++++ LE.md | 0 Zend/zend.h | 9 +++++---- Zend/zend_ast.h | 2 ++ Zend/zend_compile.h | 2 ++ Zend/zend_language_scanner.l | 20 ++------------------ Zend/zend_types.h | 10 ++++++++++ t.php | 19 +++++++++++++++++++ t2.php | 9 +++++++++ tree.php | 18 ++++++++++++++++++ 10 files changed, 75 insertions(+), 22 deletions(-) create mode 100644 Dockerfile.php create mode 100644 LE.md create mode 100644 t.php create mode 100644 t2.php create mode 100644 tree.php diff --git a/Dockerfile.php b/Dockerfile.php new file mode 100644 index 0000000000000..bcccb66b9b83f --- /dev/null +++ b/Dockerfile.php @@ -0,0 +1,8 @@ +FROM ubuntu:noble + +RUN apt update && apt install -y pkg-config build-essential autoconf bison re2c \ + libxml2-dev libsqlite3-dev cmake gdb +WORKDIR /app + +COPY ./sapi/cli/php php +RUN chmod +x /app/php \ No newline at end of file diff --git a/LE.md b/LE.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/Zend/zend.h b/Zend/zend.h index 24952e52b6572..dc3d41244363a 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -144,16 +144,17 @@ struct _zend_inheritance_cache_entry { zend_class_entry *traits_and_interfaces[1]; }; -typedef struct _zend_generic_type { +typedef struct _zend_type_parameter { struct _zend_generic_type *elements; size_t element_count; - char *type; -} zend_generic_type; + zend_string *name; +} zend_type_parameter; struct _zend_class_entry { // TYPE HERE char type; - zend_generic_type *generic_type; + size_t generic_type_count; + zend_type_parameter *generic_type; zend_string *name; /* class_entry or string depending on ZEND_ACC_LINKED */ union { diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 77ad0aa141692..c3b09a7ddb1ac 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -156,6 +156,8 @@ enum _zend_ast_kind { ZEND_AST_NAMED_ARG, ZEND_AST_PARENT_PROPERTY_HOOK_CALL, + ZEND_AST_GENERIC_TYPE, + /* 3 child nodes */ ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT, ZEND_AST_NULLSAFE_METHOD_CALL, diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 224a68be749cb..ddd71a56751b5 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -483,9 +483,11 @@ typedef struct _zend_internal_arg_info { } zend_internal_arg_info; /* arg_info for user functions */ +//LEHP-RELEVANT typedef struct _zend_arg_info { zend_string *name; zend_type type; + //LEHP-ASSUMPTION: LEPHP code evaluated at runtime zend_string *default_value; } zend_arg_info; diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 7a25ea7adf5bc..72f2f0c938ac7 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -1537,30 +1537,14 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN_WITH_IDENT(T_PRINT); } -"<"{LABEL}">" { - yyless(1); - yy_push_state(ST_GENERIC); + "!🥖" { RETURN_TOKEN(T_GENERIC_START); } -{LABEL} { - RETURN_TOKEN_WITH_STR(T_STRING, 0); -} - -"," { - RETURN_TOKEN(','); -} - -">" { - yy_pop_state(); + "!🥥" { RETURN_TOKEN(T_GENERIC_END); } -{WHITESPACE} { - HANDLE_NEWLINES(yytext, yyleng); - RETURN_TOKEN(T_WHITESPACE); -} - "class" { RETURN_TOKEN_WITH_IDENT(T_CLASS); } diff --git a/Zend/zend_types.h b/Zend/zend_types.h index f839cec3b3667..72b814d9f5d88 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -142,6 +142,7 @@ typedef struct { zend_type types[1]; } zend_type_list; +//LEHP-RELEVANT new generic type #define _ZEND_TYPE_EXTRA_FLAGS_SHIFT 25 #define _ZEND_TYPE_MASK ((1u << 25) - 1) /* Only one of these bits may be set. */ @@ -553,11 +554,20 @@ typedef struct _HashTableIterator { uint32_t next_copy; // circular linked list via index into EG(ht_iterators) } HashTableIterator; +typedef struct _zend_generic_value { + struct _zend_generic_value *elements; + size_t element_count; + zend_string *name; +} zend_generic_value; + struct _zend_object { zend_refcounted_h gc; uint32_t handle; // TODO: may be removed ??? uint32_t extra_flags; /* OBJ_EXTRA_FLAGS() */ zend_class_entry *ce; + //LEHP-OPTIMIZATION: COPY THIS FROM _zend_class_entry + size_t generic_value_count; + zend_generic_value *generic_values; const zend_object_handlers *handlers; HashTable *properties; zval properties_table[1]; diff --git a/t.php b/t.php new file mode 100644 index 0000000000000..71eb581036b5b --- /dev/null +++ b/t.php @@ -0,0 +1,19 @@ +{ + public function __construct( + public K $lol +){ + } +} + +echo "hi\n"; diff --git a/tree.php b/tree.php new file mode 100644 index 0000000000000..edbb83353027f --- /dev/null +++ b/tree.php @@ -0,0 +1,18 @@ +, $le) + echo "hi\n";'); + +var_export($tokens); From c1b0fd59f56a599cb0ce9f24e8445086e5307106 Mon Sep 17 00:00:00 2001 From: desIstUrGuad Date: Wed, 2 Apr 2025 21:42:25 +0000 Subject: [PATCH 11/15] commitierung der kochung --- LE.md | 126 ++++++++++++++++++++++++++++++++++++ Zend/zend_language_parser.y | 68 ++++++++++++++----- t.php | 2 +- tree.php | 2 +- 4 files changed, 180 insertions(+), 18 deletions(-) diff --git a/LE.md b/LE.md index e69de29bb2d1d..9411f746897f5 100644 --- a/LE.md +++ b/LE.md @@ -0,0 +1,126 @@ +# Generics syntax + +## Generic classes and interfaces + +### Basic case +```lhp +class GenericTestClass { + // T can be used as type alias inside of 'GenericTestClass' + public function __construct( + private T $property + ) {} + + public function getMyT(): T { + return $this->property; + } + + public function setMyT(T $newValue) { + $this->property = $newValue; + } +} +``` + +### Multiple Generics +```lehp +class GenericTestClass { + // T and U can now be used as type aliases inside of 'GenericTestClass' + + public function __construct( + private T $first, + private U $second + ) {} + + public function getFirst(): T { + return $this->first; + } + + public function setFirst(T $value) { + $this->first = $value; + } + + public function getSecond(): U { + return $this->second; + } + + public function setSecond(U $value) { + $this->second = $value; + } +} +``` + +### Bounded Gerics +```lehp +interface Serializable { + public function serialize(): string; +} + +class BoundedGenericClass { + public function __construct( + private T $serializable + ) {} + + public function getSerialized(): string + { + return $this->serializable->serialize(); + } + + public function getAsSerializable(): Serializable + { + return (Serializable) $this->serializable; // cast optional + } + + public function getObject(): T + { + return $this->serializable; + } +} +``` + +### A little bit more complex +``` +interface Serializer { + public function serialize(T $object): string; +} + +class RandomAssSerializableContaier> { + + private array $content = []; + + public function __construct( + private K serializer + ) {} + + public function add(T $toAdd) + { + $content[] = $toAdd; + } + + public function asSerializedList(): array + { + return array_map( + fn ($obj) $this->serializer->serialize($obj), + $this->content + ); + } +} +``` +## Functions +### Basic case +``` +function noOp(T $in): T +{ + return $in; +} +``` + +### A bit more complex +``` +interface Serializer { + public function serialize(T $object): string; +} + +function serialize>(T $in, K $serializer): string +{ + return $serializer->serialize($in); +} +``` \ No newline at end of file diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 2a7110d690f96..bbdd70f186041 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -291,10 +291,16 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type function_name non_empty_member_modifiers %type property_hook property_hook_list optional_property_hook_list hooked_property property_hook_body %type optional_parameter_list -%type generic_type_parameter_list -%type generic_type_parameters +%type nested_generic_type_parameter_list +%type nested_generic_type_parameters %type generic_type %type non_empty_generic_type_parameters +%type generic_type_with_constraint +%type generic_type_parameter_list_with_constraints +%type class_or_fuction_decl_generic_type_parameters +%type simple_generic_type +%type simple_generic_type_parameter_list +%type non_empty_simple_generic_type_parameters %type returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers %type method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers @@ -302,7 +308,6 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type backup_lex_pos %type backup_doc_comment -%type generic_param_name %type reserved_non_modifiers semi_reserved @@ -607,25 +612,60 @@ is_variadic: class_declaration_statement: class_modifiers T_CLASS { $$ = CG(zend_lineno); } - T_STRING generic_type_parameters extends_from implements_list backup_doc_comment '{' class_statement_list '}' + T_STRING class_or_fuction_decl_generic_type_parameters extends_from implements_list backup_doc_comment '{' class_statement_list '}' { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $8, zend_ast_get_str($4), $6, $7, $10, $5, NULL); } | T_CLASS { $$ = CG(zend_lineno); } - T_STRING generic_type_parameters extends_from implements_list backup_doc_comment '{' class_statement_list '}' + T_STRING class_or_fuction_decl_generic_type_parameters extends_from implements_list backup_doc_comment '{' class_statement_list '}' { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $7, zend_ast_get_str($3), $5, $6, $9, $4, NULL); } ; -generic_type_parameters: - T_GENERIC_START generic_type_parameter_list possible_comma T_GENERIC_END { $$ = $2; } +// only support e.g. "" +non_empty_simple_generic_type_parameters: + T_GENERIC_START simple_generic_type_parameter_list T_GENERIC_END { $$ = $2; } +; + +// only support e.g. "SomeType" +simple_generic_type: + T_STRING { $$ = zend_ast_create_zval_from_str(zend_ast_get_str($1)); } + | T_STRING non_empty_simple_generic_type_parameters { $$ = zend_ast_create(ZEND_AST_GENERIC_TYPE, zend_ast_create_zval_from_str(zend_ast_get_str($1)), $2); } +; + +// only support e.g. "T, S, A" +simple_generic_type_parameter_list: + T_STRING { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_TYPE_PARAM_LIST, $1); } + | generic_type_parameter_list_with_constraints ',' T_STRING { $$ = zend_ast_list_add($1, $3); } +; + +// supports bounded types, e.g. "" +class_or_fuction_decl_generic_type_parameters: + T_GENERIC_START generic_type_parameter_list_with_constraints T_GENERIC_END { $$ = $2; } | %empty { $$ = NULL; } ; -generic_type_parameter_list: +// supports bounded types, e.g. "T : SomeInterface, S : SomeClass" +generic_type_parameter_list_with_constraints: + generic_type_with_constraint { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_TYPE_PARAM_LIST, $1); } + | generic_type_parameter_list_with_constraints ',' generic_type_with_constraint { $$ = zend_ast_list_add($1, $3); } +; + +// supports bounded types, e.g. "T : SomeInterface" +generic_type_with_constraint: + T_STRING { $$ = zend_ast_create_zval_from_str(zend_ast_get_str($1)); } + | T_STRING ':' simple_generic_type { $$ = zend_ast_create(ZEND_AST_GENERIC_TYPE, zend_ast_create_zval_from_str(zend_ast_get_str($1)), $3); } +; + +nested_generic_type_parameters: + T_GENERIC_START nested_generic_type_parameter_list T_GENERIC_END { $$ = $2; } + | %empty { $$ = NULL; } +; + +nested_generic_type_parameter_list: generic_type { $$ = zend_ast_create_list(1, ZEND_AST_GENERIC_TYPE_PARAM_LIST, $1); } - | generic_type_parameter_list ',' generic_type { $$ = zend_ast_list_add($1, $3); } + | nested_generic_type_parameter_list ',' generic_type { $$ = zend_ast_list_add($1, $3); } ; non_empty_generic_type_parameters: - T_GENERIC_START generic_type_parameter_list possible_comma T_GENERIC_END { $$ = $2; } + T_GENERIC_START nested_generic_type_parameter_list T_GENERIC_END { $$ = $2; } ; generic_type: @@ -896,9 +936,9 @@ type_expr_without_static: //TODO add generic type params to AST type_without_static: - T_ARRAY generic_type_parameters { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); } + T_ARRAY nested_generic_type_parameters { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_ARRAY); } | T_CALLABLE { $$ = zend_ast_create_ex(ZEND_AST_TYPE, IS_CALLABLE); } - | name generic_type_parameters { $$ = $1; } + | name nested_generic_type_parameters { $$ = $1; } ; union_type_without_static_element: @@ -1390,10 +1430,6 @@ backup_doc_comment: %empty { $$ = CG(doc_comment); CG(doc_comment) = NULL; } ; -generic_param_name: - %empty { $$ = CG(doc_comment); CG(doc_comment) = NULL; } -; - backup_fn_flags: %prec PREC_ARROW_FUNCTION %empty { $$ = CG(extra_fn_flags); CG(extra_fn_flags) = 0; } ; diff --git a/t.php b/t.php index 71eb581036b5b..a3e35aa2b46fd 100644 --- a/t.php +++ b/t.php @@ -1,5 +1,5 @@ Date: Thu, 3 Apr 2025 18:18:40 +0000 Subject: [PATCH 12/15] .idea --- .idea/.gitignore | 8 ++ .idea/customTargets.xml | 8 ++ .idea/editor.xml | 247 ++++++++++++++++++++++++++++++++++++++++ .idea/modules.xml | 8 ++ .idea/php-src.iml | 8 ++ .idea/vcs.xml | 6 + 6 files changed, 285 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/customTargets.xml create mode 100644 .idea/editor.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/php-src.iml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000000000..13566b81b018a --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/customTargets.xml b/.idea/customTargets.xml new file mode 100644 index 0000000000000..d1c7a7505d836 --- /dev/null +++ b/.idea/customTargets.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/editor.xml b/.idea/editor.xml new file mode 100644 index 0000000000000..626ca2200383c --- /dev/null +++ b/.idea/editor.xml @@ -0,0 +1,247 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000000000..e7747caa47a8d --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php-src.iml b/.idea/php-src.iml new file mode 100644 index 0000000000000..bc2cd87409057 --- /dev/null +++ b/.idea/php-src.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000000..35eb1ddfbbc02 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From 8d5e0f80faa5708ef004370da8503ffc06d699c5 Mon Sep 17 00:00:00 2001 From: desIstUrGuad Date: Thu, 3 Apr 2025 18:47:26 +0000 Subject: [PATCH 13/15] le --- LE.md | 28 ++++++++++++++++++++++++++++ Zend/zend_language_scanner.l | 21 +++++++++++++++++---- le-test/simple-function.php | 3 +++ le-test/simple.php | 18 ++++++++++++++++++ t.php | 29 +++++++++++++++++------------ tree.php | 15 +-------------- 6 files changed, 84 insertions(+), 30 deletions(-) create mode 100644 le-test/simple-function.php create mode 100644 le-test/simple.php diff --git a/LE.md b/LE.md index 9411f746897f5..179c1986bd82a 100644 --- a/LE.md +++ b/LE.md @@ -1,5 +1,33 @@ # Generics syntax +``` +interface Comparable { + +} + +interface Equatable { + +} + +int implements Comparable, Equatable; + +class Foo { + + class Bar { + + } + +} + +enum FooBar { + + case Blum(int bam); + case Bam(T boom); + +} + +``` + ## Generic classes and interfaces ### Basic case diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index 72f2f0c938ac7..d6f6542300401 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -1395,6 +1395,7 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ } "function" { + yy_push_state(ST_IN_FUNC_DEF); RETURN_TOKEN_WITH_IDENT(T_FUNCTION); } @@ -1537,14 +1538,21 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN_WITH_IDENT(T_PRINT); } - "!🥖" { + "<" { + yy_push_state(ST_IN_GENERIC); RETURN_TOKEN(T_GENERIC_START); } - "!🥥" { + ">" { + yy_pop_state(); RETURN_TOKEN(T_GENERIC_END); } +{LABEL} { + RETURN_TOKEN_WITH_STR(T_STRING, 0); +} + + "class" { RETURN_TOKEN_WITH_IDENT(T_CLASS); } @@ -1588,7 +1596,7 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN(T_NULLSAFE_OBJECT_OPERATOR); } -{WHITESPACE}+ { +{WHITESPACE}+ { goto return_whitespace; } @@ -1916,6 +1924,11 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN(yytext[0]); } +"(" { + enter_nesting('('); + yy_pop_state(); +} + {TOKENS} { RETURN_TOKEN(yytext[0]); } @@ -2402,7 +2415,7 @@ inline_char_handler: RETURN_TOKEN(T_NS_SEPARATOR); } -{LABEL} { +{LABEL} { RETURN_TOKEN_WITH_STR(T_STRING, 0); } diff --git a/le-test/simple-function.php b/le-test/simple-function.php new file mode 100644 index 0000000000000..307fa2d5f8d03 --- /dev/null +++ b/le-test/simple-function.php @@ -0,0 +1,3 @@ +(): void {} \ No newline at end of file diff --git a/le-test/simple.php b/le-test/simple.php new file mode 100644 index 0000000000000..c91f3ec59713c --- /dev/null +++ b/le-test/simple.php @@ -0,0 +1,18 @@ + { + // T can be used as type alias inside of 'GenericTestClass' + public function __construct( + private T $property + ) {} + + public function getMyT(): T { + return $this->property; + } + + public function setMyT(T $newValue) { + $this->property = $newValue; + } +} \ No newline at end of file diff --git a/t.php b/t.php index a3e35aa2b46fd..39f17badeb824 100644 --- a/t.php +++ b/t.php @@ -1,19 +1,24 @@ $this->serializer->serialize($obj), + $this->content + ); + } +} echo "hi\n"; \ No newline at end of file diff --git a/tree.php b/tree.php index 16fb675a5a960..d8098b708d534 100644 --- a/tree.php +++ b/tree.php @@ -1,18 +1,5 @@ , $le) - echo "hi\n";'); +function noOp(): void {}'); var_export($tokens); From 06b802d109c30e6cd8978941259cc55f05004d62 Mon Sep 17 00:00:00 2001 From: "luis.carilla" Date: Sun, 6 Apr 2025 19:03:21 +0200 Subject: [PATCH 14/15] start getting AST compiled to zend_class_entry --- Zend/zend_compile.c | 56 +++++++++++++++++++++++++++++++++++++++++++-- t.php | 15 +++--------- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 75047fc029c71..f0a999a30e32b 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -9043,6 +9043,53 @@ static void zend_compile_enum_backing_type(zend_class_entry *ce, zend_ast *enum_ zend_type_release(type, 0); } +// LEHP-RELEVANT +// compiles generic class params from the AST to the zend_class_entry +static void zend_compile_class_generic_params(zend_class_entry *class_entry, zend_ast *ast) { + ZEND_ASSERT(ast->kind == ZEND_AST_GENERIC_TYPE_PARAM_LIST); + // we know this is a list as it has GENERIC_LIST_TYPE + zend_ast_list *generic_param_list = zend_ast_get_list(ast); + + class_entry->generic_type_count = generic_param_list->children; + class_entry->generic_type = ecalloc(generic_param_list->children, sizeof(zend_type_parameter)); + for (uint32_t i = 0; i < generic_param_list->children; i++) { + zend_ast *param_ast = generic_param_list->child[i]; + zend_type_parameter *type_param = &class_entry->generic_type[i]; + + if (param_ast->kind == ZEND_AST_ZVAL) { + // Simple type parameter T + zend_ast_zval *zval_ast = (zend_ast_zval*)param_ast; + if (Z_TYPE(zval_ast->val) != IS_STRING) { + zend_error_noreturn(E_COMPILE_ERROR, "Generic type parameter name must be a string"); + } + type_param->name = zend_string_copy(Z_STR(zval_ast->val)); + type_param->element_count = 0; + type_param->elements = NULL; + zend_resolve_const_class_name_reference(zval_ast, "class name"); + } else if (param_ast->kind == ZEND_AST_GENERIC_TYPE) { + // Type parameter with constraints like T:string + if (param_ast->child[0]->kind != ZEND_AST_ZVAL) { + zend_error_noreturn(E_COMPILE_ERROR, "Generic type parameter name must be a simple identifier"); + } + + zend_ast_zval *name_ast = (zend_ast_zval*)param_ast->child[0]; + if (Z_TYPE(name_ast->val) != IS_STRING) { + zend_error_noreturn(E_COMPILE_ERROR, "Generic type parameter name must be a string"); + } + + type_param->name = zend_string_copy(Z_STR(name_ast->val)); + + // LEHP-TODO add constraints to model + type_param->element_count = 0; + type_param->elements = NULL; + + // TODO: In the future, process constraint list from param_ast->child[1] + } else { + zend_error_noreturn(E_COMPILE_ERROR, "Invalid generic type parameter"); + } + } +} + static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) /* {{{ */ { zend_ast_decl *decl = (zend_ast_decl *) ast; @@ -9133,8 +9180,13 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) CG(active_class_entry) = ce; - if (decl->child[3] && decl->child[3]->kind != ZEND_AST_GENERIC_TYPE_PARAM_LIST) { - zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS, 0); + if (decl->child[3]) { + if(decl->child[3]->kind == ZEND_AST_GENERIC_TYPE_PARAM_LIST){ + zend_compile_class_generic_params(ce, decl->child[3]); + } else { + // LEHP-TODO this should never happen, attributes are never passed as third child to zend_ast_create_decl(ZEND_AST_CLASS, ...)??? + zend_compile_attributes(&ce->attributes, decl->child[3], 0, ZEND_ATTRIBUTE_TARGET_CLASS, 0); + } } if (implements_ast) { diff --git a/t.php b/t.php index a3e35aa2b46fd..7619145823363 100644 --- a/t.php +++ b/t.php @@ -1,19 +1,10 @@ Date: Sat, 10 May 2025 16:07:29 +0000 Subject: [PATCH 15/15] wip --- Zend/zend_language_scanner.l | 34 +++++++++++++++++++----- le-test/{simple.php => simple-class.php} | 4 +-- t.php | 2 +- tree.php | 11 +++++--- 4 files changed, 37 insertions(+), 14 deletions(-) rename le-test/{simple.php => simple-class.php} (87%) diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index d6f6542300401..aa3381a8a580e 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -1538,12 +1538,12 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN_WITH_IDENT(T_PRINT); } - "<" { + "<" { yy_push_state(ST_IN_GENERIC); RETURN_TOKEN(T_GENERIC_START); } - ">" { + ">" { yy_pop_state(); RETURN_TOKEN(T_GENERIC_END); } @@ -1554,9 +1554,19 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ "class" { + yy_push_state(ST_IN_CLASS_DEF); RETURN_TOKEN_WITH_IDENT(T_CLASS); } + + +"{" { + yy_pop_state(); + yy_push_state(ST_IN_SCRIPTING); + enter_nesting('{'); + RETURN_TOKEN('{'); +} + "interface" { RETURN_TOKEN_WITH_IDENT(T_INTERFACE); } @@ -1578,11 +1588,11 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN_WITH_IDENT(T_ENUM); } -"extends" { +"extends" { RETURN_TOKEN_WITH_IDENT(T_EXTENDS); } -"implements" { +"implements" { RETURN_TOKEN_WITH_IDENT(T_IMPLEMENTS); } @@ -1596,7 +1606,7 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ RETURN_TOKEN(T_NULLSAFE_OBJECT_OPERATOR); } -{WHITESPACE}+ { +{WHITESPACE}+ { goto return_whitespace; } @@ -1738,9 +1748,14 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ } "private" { + yy_push_state(ST_IN_PROPERTY_TYPE_DEF); RETURN_TOKEN_WITH_IDENT(T_PRIVATE); } + { + +} + "protected" { RETURN_TOKEN_WITH_IDENT(T_PROTECTED); } @@ -1925,8 +1940,13 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_ } "(" { - enter_nesting('('); yy_pop_state(); + enter_nesting(yytext[0]); + RETURN_TOKEN(yytext[0]); +} + +":"|"," { + RETURN_TOKEN(yytext[0]); } {TOKENS} { @@ -2415,7 +2435,7 @@ inline_char_handler: RETURN_TOKEN(T_NS_SEPARATOR); } -{LABEL} { +{LABEL} { RETURN_TOKEN_WITH_STR(T_STRING, 0); } diff --git a/le-test/simple.php b/le-test/simple-class.php similarity index 87% rename from le-test/simple.php rename to le-test/simple-class.php index c91f3ec59713c..73423ce0166f2 100644 --- a/le-test/simple.php +++ b/le-test/simple-class.php @@ -1,8 +1,6 @@ { +class GenericTestClass { // T can be used as type alias inside of 'GenericTestClass' public function __construct( private T $property diff --git a/t.php b/t.php index 39f17badeb824..50eb67b5c605b 100644 --- a/t.php +++ b/t.php @@ -1,6 +1,6 @@ > { private array $content = []; diff --git a/tree.php b/tree.php index d8098b708d534..934f17b7dd468 100644 --- a/tree.php +++ b/tree.php @@ -1,5 +1,10 @@ { + public Serializable $test; + function __construct() {} +} +'); -var_export($tokens); +var_export($classTokens); \ No newline at end of file