From 583fec1e67e77ff77604aeff8f59faf8e2dd08bb Mon Sep 17 00:00:00 2001 From: Ilya Trubachev Date: Thu, 27 Jul 2023 17:03:11 +0300 Subject: [PATCH] Add enum explicit initializers Signed-off-by: Ilya Trubachev --- checker/types/ets/etsEnumType.cpp | 5 +- ir/ts/tsEnumMember.h | 6 + parser/ETSparser.cpp | 45 +- parser/ETSparser.h | 2 + .../ets/enum-initialized-expected.txt | 590 ++++++++++++++++++ test/compiler/ets/enum-initialized.ets | 26 + 6 files changed, 670 insertions(+), 4 deletions(-) create mode 100644 test/compiler/ets/enum-initialized-expected.txt create mode 100644 test/compiler/ets/enum-initialized.ets diff --git a/checker/types/ets/etsEnumType.cpp b/checker/types/ets/etsEnumType.cpp index b1d7e5126..041b33eb2 100644 --- a/checker/types/ets/etsEnumType.cpp +++ b/checker/types/ets/etsEnumType.cpp @@ -20,6 +20,7 @@ #include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" #include "plugins/ecmascript/es2panda/ir/expressions/literals/numberLiteral.h" #include "plugins/ecmascript/es2panda/ir/expressions/memberExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/unaryExpression.h" #include "plugins/ecmascript/es2panda/ir/ts/tsEnumMember.h" namespace panda::es2panda::checker { @@ -107,7 +108,9 @@ util::StringView ETSEnumType::GetName() const noexcept ETSEnumType::UType ETSEnumType::GetOrdinal() const { ASSERT(IsLiteralType()); - auto ordinal = member_->Init()->AsNumberLiteral()->Number().GetInt(); + auto ordinal = member_->Init()->IsNumberLiteral() + ? member_->Init()->AsNumberLiteral()->Number().GetInt() + : (-1) * member_->Init()->AsUnaryExpression()->Argument()->AsNumberLiteral()->Number().GetInt(); static_assert(std::is_same_v); return ordinal; } diff --git a/ir/ts/tsEnumMember.h b/ir/ts/tsEnumMember.h index 6dc9fb4fb..78afcbecd 100644 --- a/ir/ts/tsEnumMember.h +++ b/ir/ts/tsEnumMember.h @@ -37,6 +37,12 @@ public: { return init_; } + + void SetInit(Expression *init) + { + init_ = init; + } + void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; diff --git a/parser/ETSparser.cpp b/parser/ETSparser.cpp index 6c954787a..13c48c8ef 100644 --- a/parser/ETSparser.cpp +++ b/parser/ETSparser.cpp @@ -3555,6 +3555,24 @@ ir::TSTypeParameter *ETSParser::ParseTypeParameter([[maybe_unused]] TypeAnnotati return type_param; } +int32_t ETSParser::AssignEnumMemberInitializer(ArenaVector &used_cardinals, + ArenaVector &members, int32_t start_value, size_t pos) +{ + while (std::find_if(used_cardinals.begin(), used_cardinals.end(), [&start_value](ir::Expression *expr) { + int32_t compare_value = + expr->IsNumberLiteral() + ? expr->AsNumberLiteral()->Number().GetInt() + : (-1) * expr->AsUnaryExpression()->Argument()->AsNumberLiteral()->Number().GetInt(); + return start_value == compare_value; + }) != used_cardinals.end()) { + start_value++; + } + auto *const cardinal_node = AllocNode(lexer::Number(start_value)); + used_cardinals.push_back(cardinal_node); + members[pos]->AsTSEnumMember()->SetInit(cardinal_node); + return start_value; +} + ir::TSEnumDeclaration *ETSParser::ParseEnumMembers(ir::Identifier *const key, const lexer::SourcePosition &enum_start, const bool is_const, const bool is_static) { @@ -3569,16 +3587,27 @@ ir::TSEnumDeclaration *ETSParser::ParseEnumMembers(ir::Identifier *const key, co } ArenaVector members(Allocator()->Adapter()); + ArenaVector member_initializers(Allocator()->Adapter()); const auto enum_ctx = binder::LexicalScope(Binder()); - auto parse_member = [this, &members, ordinal_number = uint32_t {0}]() mutable { + auto parse_member = [this, &members, &member_initializers /*, ordinal_number = uint32_t {0} */]() mutable { auto *const ident = ExpectIdentifier(); auto [decl, var] = Binder()->NewVarDecl(ident->Start(), ident->Name()); var->SetScope(Binder()->GetScope()); var->AddFlag(binder::VariableFlags::STATIC); ident->SetVariable(var); - auto *const ordinal = AllocNode(lexer::Number(ordinal_number++)); - auto *const member = AllocNode(ident, ordinal); + ir::Expression *initializer = nullptr; + if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) { + Lexer()->NextToken(); // eat '=' + initializer = ParseExpression(); + if ((!initializer->IsNumberLiteral() || !initializer->AsNumberLiteral()->Number().IsInt()) && + (!initializer->IsUnaryExpression() || + !initializer->AsUnaryExpression()->Argument()->AsNumberLiteral()->Number().IsInt())) { + ThrowSyntaxError("Enum initializer must be signed 32-bit integer"); + } + member_initializers.push_back(initializer); + } + auto *const member = AllocNode(ident, initializer); member->SetRange(ident->Range()); decl->BindNode(member); members.push_back(member); @@ -3600,6 +3629,16 @@ ir::TSEnumDeclaration *ETSParser::ParseEnumMembers(ir::Identifier *const key, co parse_member(); } + int32_t start_cardinal = 0; + if (members[0]->AsTSEnumMember()->Init() == nullptr) { + start_cardinal = AssignEnumMemberInitializer(member_initializers, members, start_cardinal, 0); + } + for (size_t i = 1; i < members.size(); ++i) { + if (members[i]->AsTSEnumMember()->Init() == nullptr) { + start_cardinal = AssignEnumMemberInitializer(member_initializers, members, start_cardinal, i); + } + } + auto *const enum_declaration = AllocNode(Allocator(), Binder()->GetScope()->AsLocalScope(), key, std::move(members), is_const, is_static); enum_declaration->SetRange({enum_start, Lexer()->GetToken().End()}); diff --git a/parser/ETSparser.h b/parser/ETSparser.h index 21008f28c..2421df26f 100644 --- a/parser/ETSparser.h +++ b/parser/ETSparser.h @@ -152,6 +152,8 @@ private: ir::Expression *ParseAsyncExpression(); ir::Expression *ParseAwaitExpression(); ir::TSTypeParameter *ParseTypeParameter(TypeAnnotationParsingOptions *options) override; + int32_t AssignEnumMemberInitializer(ArenaVector &used_cardinals, + ArenaVector &members, int32_t start_value, size_t pos); ir::TSEnumDeclaration *ParseEnumMembers(ir::Identifier *key, const lexer::SourcePosition &enum_start, bool is_const, bool is_static) override; ir::Statement *ParseInterfaceDeclaration(bool is_static) override; diff --git a/test/compiler/ets/enum-initialized-expected.txt b/test/compiler/ets/enum-initialized-expected.txt new file mode 100644 index 000000000..7c093f6b3 --- /dev/null +++ b/test/compiler/ets/enum-initialized-expected.txt @@ -0,0 +1,590 @@ +{ + "type": "Program", + "statements": [ + { + "type": "TSEnumDeclaration", + "id": { + "type": "Identifier", + "name": "Color", + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 6 + }, + "end": { + "line": 16, + "column": 11 + } + } + }, + "members": [ + { + "type": "TSEnumMember", + "id": { + "type": "Identifier", + "name": "red", + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 5 + }, + "end": { + "line": 17, + "column": 8 + } + } + }, + "initializer": { + "type": "NumberLiteral", + "value": 1, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 5 + }, + "end": { + "line": 17, + "column": 8 + } + } + }, + { + "type": "TSEnumMember", + "id": { + "type": "Identifier", + "name": "green", + "decorators": [], + "loc": { + "start": { + "line": 18, + "column": 5 + }, + "end": { + "line": 18, + "column": 10 + } + } + }, + "initializer": { + "type": "NumberLiteral", + "value": 0, + "loc": { + "start": { + "line": 18, + "column": 13 + }, + "end": { + "line": 18, + "column": 14 + } + } + }, + "loc": { + "start": { + "line": 18, + "column": 5 + }, + "end": { + "line": 18, + "column": 10 + } + } + }, + { + "type": "TSEnumMember", + "id": { + "type": "Identifier", + "name": "blue", + "decorators": [], + "loc": { + "start": { + "line": 19, + "column": 5 + }, + "end": { + "line": 19, + "column": 9 + } + } + }, + "initializer": { + "type": "NumberLiteral", + "value": 3, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 5 + }, + "end": { + "line": 19, + "column": 9 + } + } + }, + { + "type": "TSEnumMember", + "id": { + "type": "Identifier", + "name": "yellow", + "decorators": [], + "loc": { + "start": { + "line": 20, + "column": 5 + }, + "end": { + "line": 20, + "column": 11 + } + } + }, + "initializer": { + "type": "NumberLiteral", + "value": 2, + "loc": { + "start": { + "line": 20, + "column": 14 + }, + "end": { + "line": 20, + "column": 15 + } + } + }, + "loc": { + "start": { + "line": 20, + "column": 5 + }, + "end": { + "line": 20, + "column": 11 + } + } + }, + { + "type": "TSEnumMember", + "id": { + "type": "Identifier", + "name": "deep", + "decorators": [], + "loc": { + "start": { + "line": 21, + "column": 5 + }, + "end": { + "line": 21, + "column": 9 + } + } + }, + "initializer": { + "type": "NumberLiteral", + "value": 25, + "loc": { + "start": { + "line": 21, + "column": 12 + }, + "end": { + "line": 21, + "column": 14 + } + } + }, + "loc": { + "start": { + "line": 21, + "column": 5 + }, + "end": { + "line": 21, + "column": 9 + } + } + }, + { + "type": "TSEnumMember", + "id": { + "type": "Identifier", + "name": "purple", + "decorators": [], + "loc": { + "start": { + "line": 22, + "column": 5 + }, + "end": { + "line": 22, + "column": 11 + } + } + }, + "initializer": { + "type": "NumberLiteral", + "value": 4, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 22, + "column": 5 + }, + "end": { + "line": 22, + "column": 11 + } + } + }, + { + "type": "TSEnumMember", + "id": { + "type": "Identifier", + "name": "black", + "decorators": [], + "loc": { + "start": { + "line": 23, + "column": 5 + }, + "end": { + "line": 23, + "column": 10 + } + } + }, + "initializer": { + "type": "UnaryExpression", + "operator": "-", + "prefix": true, + "argument": { + "type": "NumberLiteral", + "value": 42, + "loc": { + "start": { + "line": 23, + "column": 14 + }, + "end": { + "line": 23, + "column": 16 + } + } + }, + "loc": { + "start": { + "line": 23, + "column": 13 + }, + "end": { + "line": 23, + "column": 16 + } + } + }, + "loc": { + "start": { + "line": 23, + "column": 5 + }, + "end": { + "line": 23, + "column": 10 + } + } + }, + { + "type": "TSEnumMember", + "id": { + "type": "Identifier", + "name": "white", + "decorators": [], + "loc": { + "start": { + "line": 24, + "column": 5 + }, + "end": { + "line": 24, + "column": 10 + } + } + }, + "initializer": { + "type": "NumberLiteral", + "value": 5, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 24, + "column": 5 + }, + "end": { + "line": 24, + "column": 10 + } + } + }, + { + "type": "TSEnumMember", + "id": { + "type": "Identifier", + "name": "gray", + "decorators": [], + "loc": { + "start": { + "line": 25, + "column": 5 + }, + "end": { + "line": 25, + "column": 9 + } + } + }, + "initializer": { + "type": "NumberLiteral", + "value": 255, + "loc": { + "start": { + "line": 25, + "column": 12 + }, + "end": { + "line": 25, + "column": 16 + } + } + }, + "loc": { + "start": { + "line": 25, + "column": 5 + }, + "end": { + "line": 25, + "column": 9 + } + } + } + ], + "const": false, + "loc": { + "start": { + "line": 16, + "column": 1 + }, + "end": { + "line": 26, + "column": 2 + } + } + }, + { + "type": "ClassDeclaration", + "definition": { + "id": { + "type": "Identifier", + "name": "ETSGLOBAL", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "superClass": null, + "implements": [], + "body": [ + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "kind": "method", + "accessibility": "public", + "static": true, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [], + "returnType": { + "type": "ETSPrimitiveType", + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "body": { + "type": "BlockStatement", + "statements": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "overloads": [], + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 27, + "column": 1 + } + } +} diff --git a/test/compiler/ets/enum-initialized.ets b/test/compiler/ets/enum-initialized.ets new file mode 100644 index 000000000..bfb4ade51 --- /dev/null +++ b/test/compiler/ets/enum-initialized.ets @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +enum Color { + red, + green = 0, + blue, + yellow = 2, + deep = 25, + purple, + black = -42, + white, + gray = 0xFF +} -- Gitee