From 9dece1ae05fecbafc0da1f4c558a9a16a7a574ec Mon Sep 17 00:00:00 2001 From: i582 <51853996+i582@users.noreply.github.com> Date: Thu, 14 Oct 2021 02:42:45 +0300 Subject: [PATCH 1/3] initial parser attribute support --- compiler/debug.cpp | 1 + compiler/gentree.cpp | 52 ++++++++++++++++++ compiler/gentree.h | 4 ++ compiler/lexer.cpp | 6 +++ compiler/phpdoc.cpp | 21 +++++++- compiler/token.h | 2 + tests/cpp/compiler/lexer-test.cpp | 7 +++ .../php8/attributes/001_class_attributes.php | 37 +++++++++++++ .../attributes/002_interface_attributes.php | 31 +++++++++++ .../attributes/003_function_attributes.php | 25 +++++++++ .../php8/attributes/004_param_attributes.php | 53 +++++++++++++++++++ .../attributes/005_property_attributes.php | 32 +++++++++++ .../attributes/006_constant_attributes.php | 32 +++++++++++ 13 files changed, 301 insertions(+), 2 deletions(-) create mode 100644 tests/phpt/php8/attributes/001_class_attributes.php create mode 100644 tests/phpt/php8/attributes/002_interface_attributes.php create mode 100644 tests/phpt/php8/attributes/003_function_attributes.php create mode 100644 tests/phpt/php8/attributes/004_param_attributes.php create mode 100644 tests/phpt/php8/attributes/005_property_attributes.php create mode 100644 tests/phpt/php8/attributes/006_constant_attributes.php diff --git a/compiler/debug.cpp b/compiler/debug.cpp index 0164a5f4d3..b3a784f54c 100644 --- a/compiler/debug.cpp +++ b/compiler/debug.cpp @@ -184,6 +184,7 @@ std::string debugTokenName(TokenType t) { {tok_phpdoc, "tok_phpdoc"}, {tok_clone, "tok_clone"}, {tok_instanceof, "tok_instanceof"}, + {tok_attribute_start, "tok_attribute_start"}, {tok_end, "tok_end"}, }); diff --git a/compiler/gentree.cpp b/compiler/gentree.cpp index aebdcd0ddc..707a8aa17d 100644 --- a/compiler/gentree.cpp +++ b/compiler/gentree.cpp @@ -875,6 +875,10 @@ VertexPtr GenTree::get_def_value() { VertexAdaptor GenTree::get_func_param() { auto location = auto_location(); + if (test_expect(tok_attribute_start)) { + get_attribute_group(); + } + const TypeHint *type_hint = get_typehint(); bool is_varg = false; @@ -2139,6 +2143,8 @@ VertexPtr GenTree::get_statement(vk::string_view phpdoc_str) { return get_foreach(); case tok_switch: return get_switch(); + case tok_attribute_start: + return get_attribute_group(); case tok_phpdoc: { // enter /** ... */ // is it a function/class/field phpdoc? auto next = (cur+1)->type(); @@ -2233,6 +2239,52 @@ VertexPtr GenTree::get_statement(vk::string_view phpdoc_str) { kphp_fail(); } +VertexAdaptor GenTree::get_attribute_group() { + get_attribute(); + while (test_expect(tok_attribute_start)) { + get_attribute(); + } + return VertexAdaptor::create(); +} + +VertexAdaptor GenTree::get_attribute() { + next_cur(); + auto calls = std::vector>(); + const auto ok = gen_list(&calls, &GenTree::get_attribute_call, tok_comma); + CE(!kphp_error(ok, "Failed to parse attribute call")); + CE(expect(tok_clbrk, "']'")); + + return VertexAdaptor::create(); +} + +VertexAdaptor GenTree::get_attribute_call() { + const auto location = auto_location(); + if (cur->type() == tok_clbrk) { + return {}; + } + + const auto name = std::string(cur->str_val); + next_cur(); + + VertexAdaptor call; + + if (test_expect(tok_oppar)) { + CE (expect(tok_oppar, "'('")); + skip_phpdoc_tokens(); + vector next; + bool ok_next = gen_list(&next, &GenTree::get_expression, tok_comma); + CE (!kphp_error(ok_next, "get argument list failed")); + CE (expect(tok_clpar, "')'")); + + call = VertexAdaptor::create_vararg(next).set_location(location); + } + + call = VertexAdaptor::create_vararg().set_location(location); + + call->set_string(name); + return call; +} + VertexPtr GenTree::get_const_after_explicit_access_modifier(AccessModifiers access) { CE(!kphp_error(cur_class, "Access modifier for const allowed in class only")); if (cur_class->is_interface()) { diff --git a/compiler/gentree.h b/compiler/gentree.h index 31fe78c097..9462a759c5 100644 --- a/compiler/gentree.h +++ b/compiler/gentree.h @@ -128,6 +128,10 @@ class GenTree { VertexAdaptor get_anonymous_function(TokenType tok = tok_function, bool is_static = false); VertexAdaptor get_function(TokenType tok, vk::string_view phpdoc_str, FunctionModifiers modifiers, std::vector> *uses_of_lambda = nullptr); + VertexAdaptor get_attribute_group(); + VertexAdaptor get_attribute(); + VertexAdaptor get_attribute_call(); + ClassMemberModifiers parse_class_member_modifier_mask(); VertexPtr get_class_member(vk::string_view phpdoc_str); diff --git a/compiler/lexer.cpp b/compiler/lexer.cpp index 01dae66c9d..0cad431322 100644 --- a/compiler/lexer.cpp +++ b/compiler/lexer.cpp @@ -902,6 +902,12 @@ bool TokenLexerComment::parse(LexerData *lexer_data) const { const char *s = lexer_data->get_code(), *st = s; + if (s[0] == '#' && s[1] == '[') { + lexer_data->add_token(0, tok_attribute_start, s, s+2); + lexer_data->pass(2); + return vk::singleton::get().parse(lexer_data); + } + assert (s[0] == '/' || s[0] == '#'); if (s[0] == '#' || s[1] == '/') { while (s[0] && s[0] != '\n') { diff --git a/compiler/phpdoc.cpp b/compiler/phpdoc.cpp index 5424bf6b71..af922692ec 100644 --- a/compiler/phpdoc.cpp +++ b/compiler/phpdoc.cpp @@ -437,18 +437,24 @@ const TypeHint *PhpDocTypeRuleParser::parse_type_expression() { bool was_raw_bool = false; bool was_false = false; bool was_null = false; - + bool was_void = false; + auto on_each_item = [&](const TypeHint *item) { items.emplace_back(item); if (const auto *as_primitive = item->try_as()) { was_raw_bool |= as_primitive->ptype == tp_bool && (cur_tok - 1)->type() == tok_bool; was_false |= as_primitive->ptype == tp_False; was_null |= as_primitive->ptype == tp_Null; + was_void |= as_primitive->ptype == tp_void; } }; on_each_item(result); while (cur_tok->type() == tok_or) { + if (was_void) { + throw std::runtime_error("Void can only be used as a standalone type"); + } + cur_tok++; on_each_item(parse_type_array()); @@ -457,7 +463,18 @@ const TypeHint *PhpDocTypeRuleParser::parse_type_expression() { } } - // try to simplify: T|false and similar as an optional, not as a vector +// if (was_null) { +// std::vector items_without_null; +// for (const TypeHint *item : items) { +// const auto *as_primitive = item->try_as(); +// if (!as_primitive || vk::none_of_equal(as_primitive->ptype, tp_Null, tp_False)) { +// items_without_null.push_back(item); +// } +// } +// return TypeHintOptional::create(TypeHintPipe::create(std::move(items_without_null)), was_null, was_false); +// } + + // try to simplify: T|false and similar to an optional, not as a vector bool can_be_simplified_as_optional = 1 == (items.size() - was_false - was_null); if (can_be_simplified_as_optional) { for (const TypeHint *item : items) { diff --git a/compiler/token.h b/compiler/token.h index fecd6473d6..179cce83f2 100644 --- a/compiler/token.h +++ b/compiler/token.h @@ -185,6 +185,8 @@ enum TokenType { tok_clone, tok_instanceof, + tok_attribute_start, + tok_end }; diff --git a/tests/cpp/compiler/lexer-test.cpp b/tests/cpp/compiler/lexer-test.cpp index 5a437b887a..98587fe357 100644 --- a/tests/cpp/compiler/lexer-test.cpp +++ b/tests/cpp/compiler/lexer-test.cpp @@ -113,6 +113,13 @@ TEST(lexer_test, test_php_tokens) { // combined tests {"echo \"{$x->y}\";", {"tok_echo(echo)", "tok_str_begin(\")", "tok_expr_begin({)", "tok_var_name($x)", "tok_arrow(->)", "tok_func_name(y)", "tok_expr_end(})", "tok_str_end(\")", "tok_semicolon(;)"}}, + + // attributes + {"#[Attribute]", {"tok_attribute_start(#[)", "tok_func_name(Attribute)", "tok_clbrk(])"}}, + {"#[\nAttribute\n]", {"tok_attribute_start(#[)", "tok_func_name(Attribute)", "tok_clbrk(])"}}, + {"#[Attribute()]", {"tok_attribute_start(#[)", "tok_func_name(Attribute)", "tok_oppar(()", "tok_clpar())", "tok_clbrk(])"}}, + {"#[Attribute('name')]", {"tok_attribute_start(#[)", "tok_func_name(Attribute)", "tok_oppar(()", "tok_str(name)", "tok_clpar())", "tok_clbrk(])"}}, + {"#[Attribute(), Attribute2()]", {"tok_attribute_start(#[)", "tok_func_name(Attribute)", "tok_oppar(()", "tok_clpar())", "tok_comma(,)", "tok_func_name(Attribute2)", "tok_oppar(()", "tok_clpar())", "tok_clbrk(])"}}, }; for (const auto &test : tests) { diff --git a/tests/phpt/php8/attributes/001_class_attributes.php b/tests/phpt/php8/attributes/001_class_attributes.php new file mode 100644 index 0000000000..56ba5210e8 --- /dev/null +++ b/tests/phpt/php8/attributes/001_class_attributes.php @@ -0,0 +1,37 @@ +@ok php8 + Date: Thu, 14 Oct 2021 02:47:25 +0300 Subject: [PATCH 2/3] removed old code --- compiler/phpdoc.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/compiler/phpdoc.cpp b/compiler/phpdoc.cpp index af922692ec..9f4398db20 100644 --- a/compiler/phpdoc.cpp +++ b/compiler/phpdoc.cpp @@ -437,7 +437,6 @@ const TypeHint *PhpDocTypeRuleParser::parse_type_expression() { bool was_raw_bool = false; bool was_false = false; bool was_null = false; - bool was_void = false; auto on_each_item = [&](const TypeHint *item) { items.emplace_back(item); @@ -445,16 +444,11 @@ const TypeHint *PhpDocTypeRuleParser::parse_type_expression() { was_raw_bool |= as_primitive->ptype == tp_bool && (cur_tok - 1)->type() == tok_bool; was_false |= as_primitive->ptype == tp_False; was_null |= as_primitive->ptype == tp_Null; - was_void |= as_primitive->ptype == tp_void; } }; on_each_item(result); while (cur_tok->type() == tok_or) { - if (was_void) { - throw std::runtime_error("Void can only be used as a standalone type"); - } - cur_tok++; on_each_item(parse_type_array()); @@ -463,16 +457,6 @@ const TypeHint *PhpDocTypeRuleParser::parse_type_expression() { } } -// if (was_null) { -// std::vector items_without_null; -// for (const TypeHint *item : items) { -// const auto *as_primitive = item->try_as(); -// if (!as_primitive || vk::none_of_equal(as_primitive->ptype, tp_Null, tp_False)) { -// items_without_null.push_back(item); -// } -// } -// return TypeHintOptional::create(TypeHintPipe::create(std::move(items_without_null)), was_null, was_false); -// } // try to simplify: T|false and similar to an optional, not as a vector bool can_be_simplified_as_optional = 1 == (items.size() - was_false - was_null); From adf1827bb646f6ff55cdca4c05c6dee4516aab46 Mon Sep 17 00:00:00 2001 From: i582 <51853996+i582@users.noreply.github.com> Date: Thu, 14 Oct 2021 02:54:37 +0300 Subject: [PATCH 3/3] remove old code --- compiler/phpdoc.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/phpdoc.cpp b/compiler/phpdoc.cpp index 9f4398db20..61144ca2da 100644 --- a/compiler/phpdoc.cpp +++ b/compiler/phpdoc.cpp @@ -457,7 +457,6 @@ const TypeHint *PhpDocTypeRuleParser::parse_type_expression() { } } - // try to simplify: T|false and similar to an optional, not as a vector bool can_be_simplified_as_optional = 1 == (items.size() - was_false - was_null); if (can_be_simplified_as_optional) {