From f540d4a43e6025b97a9ce4776cd14b791c97b0a6 Mon Sep 17 00:00:00 2001 From: Zelentsov Dmitry Date: Wed, 18 Oct 2023 11:27:40 +0300 Subject: [PATCH] Implement formatted AST-nodes creation. Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/I8FT6U?from=project-issue Test: build Signed-off-by: Zelentsov Dmitry --- ets2panda/BUILD.gn | 1 + ets2panda/CMakeLists.txt | 1 + ets2panda/checker/ETSAnalyzer.cpp | 7 + ets2panda/checker/TSAnalyzer.cpp | 6 + ets2panda/checker/ets/helpers.cpp | 2 +- ets2panda/compiler/core/ETSCompiler.cpp | 8 +- ets2panda/compiler/core/JSCompiler.cpp | 8 +- ets2panda/compiler/core/compilerContext.h | 48 ++-- ets2panda/compiler/core/compilerImpl.cpp | 5 +- .../compiler/lowering/ets/opAssignment.cpp | 242 ++++++++---------- ets2panda/compiler/lowering/util.cpp | 19 +- ets2panda/compiler/lowering/util.h | 3 +- ets2panda/ir/astNodeMapping.h | 3 +- ets2panda/ir/base/property.cpp | 6 +- ets2panda/ir/base/property.h | 2 +- ets2panda/ir/expressions/blockExpression.cpp | 101 ++++++++ ets2panda/ir/expressions/blockExpression.h | 60 +++++ ets2panda/ir/expressions/memberExpression.cpp | 7 +- ets2panda/ir/expressions/memberExpression.h | 2 +- ets2panda/lexer/lexer.cpp | 12 +- ets2panda/lexer/lexer.h | 1 + ets2panda/lexer/token/token.cpp | 2 + ets2panda/lexer/token/tokenType.h | 1 + ets2panda/parser/ETSparser.cpp | 234 ++++++++++++++++- ets2panda/parser/ETSparser.h | 84 +++++- ets2panda/parser/TypedParser.cpp | 4 +- ets2panda/parser/parserImpl.cpp | 4 + ets2panda/parser/parserImpl.h | 29 ++- ets2panda/public/es2panda_lib.cpp | 1 + .../test/runtime/ets/assignment_lowering.ets | 43 ++++ ets2panda/varbinder/ETSBinder.cpp | 36 ++- ets2panda/varbinder/ETSBinder.h | 5 +- 32 files changed, 780 insertions(+), 207 deletions(-) create mode 100644 ets2panda/ir/expressions/blockExpression.cpp create mode 100644 ets2panda/ir/expressions/blockExpression.h create mode 100644 ets2panda/test/runtime/ets/assignment_lowering.ets diff --git a/ets2panda/BUILD.gn b/ets2panda/BUILD.gn index 26f273d668..6a29659a5f 100644 --- a/ets2panda/BUILD.gn +++ b/ets2panda/BUILD.gn @@ -202,6 +202,7 @@ libes2panda_sources = [ "ir/expressions/assignmentExpression.cpp", "ir/expressions/awaitExpression.cpp", "ir/expressions/binaryExpression.cpp", + "ir/expressions/blockExpression.cpp", "ir/expressions/callExpression.cpp", "ir/expressions/chainExpression.cpp", "ir/expressions/classExpression.cpp", diff --git a/ets2panda/CMakeLists.txt b/ets2panda/CMakeLists.txt index 75f77a2394..463ac9740c 100644 --- a/ets2panda/CMakeLists.txt +++ b/ets2panda/CMakeLists.txt @@ -166,6 +166,7 @@ set(ES2PANDA_LIB_SRC ir/expressions/assignmentExpression.cpp ir/expressions/awaitExpression.cpp ir/expressions/binaryExpression.cpp + ir/expressions/blockExpression.cpp ir/expressions/callExpression.cpp ir/expressions/chainExpression.cpp ir/expressions/classExpression.cpp diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index f91ee6ca74..e634d1e22d 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -20,6 +20,7 @@ #include "checker/ETSchecker.h" #include "checker/ets/castingContext.h" #include "checker/ets/typeRelationContext.h" +#include "ir/astNode.h" #include "ir/base/catchClause.h" #include "ir/base/classProperty.h" #include "ir/base/classStaticBlock.h" @@ -382,6 +383,12 @@ checker::Type *ETSAnalyzer::Check(ir::BinaryExpression *expr) const UNREACHABLE(); } +checker::Type *ETSAnalyzer::Check(ir::BlockExpression *st) const +{ + (void)st; + UNREACHABLE(); +} + checker::Type *ETSAnalyzer::Check(ir::CallExpression *expr) const { (void)expr; diff --git a/ets2panda/checker/TSAnalyzer.cpp b/ets2panda/checker/TSAnalyzer.cpp index d3e96e821f..06867b6c1e 100644 --- a/ets2panda/checker/TSAnalyzer.cpp +++ b/ets2panda/checker/TSAnalyzer.cpp @@ -279,6 +279,12 @@ checker::Type *TSAnalyzer::Check(ir::BinaryExpression *expr) const UNREACHABLE(); } +checker::Type *TSAnalyzer::Check(ir::BlockExpression *st) const +{ + (void)st; + UNREACHABLE(); +} + checker::Type *TSAnalyzer::Check(ir::CallExpression *expr) const { (void)expr; diff --git a/ets2panda/checker/ets/helpers.cpp b/ets2panda/checker/ets/helpers.cpp index b1f99ba5be..e9d61a4c79 100644 --- a/ets2panda/checker/ets/helpers.cpp +++ b/ets2panda/checker/ets/helpers.cpp @@ -2282,7 +2282,7 @@ bool ETSChecker::TryTransformingToStaticInvoke(ir::Identifier *const ident, cons parser::Program program(Allocator(), VarBinder()); es2panda::CompilerOptions options; auto parser = parser::ETSParser(&program, options, parser::ParserStatus::NO_OPTS); - auto *arg_expr = parser.CreateExpression(parser::ExpressionParseFlags::NO_OPTS, implicit_instantiate_argument); + auto *arg_expr = parser.CreateExpression(implicit_instantiate_argument); arg_expr->SetParent(call_expr); arg_expr->SetRange(ident->Range()); diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index 46dd096ae8..547e0aafb5 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -280,6 +280,12 @@ void ETSCompiler::Compile(const ir::BinaryExpression *expr) const UNREACHABLE(); } +void ETSCompiler::Compile(const ir::BlockExpression *expr) const +{ + (void)expr; + UNREACHABLE(); +} + void ETSCompiler::Compile(const ir::CallExpression *expr) const { (void)expr; @@ -978,4 +984,4 @@ void ETSCompiler::Compile(const ir::TSVoidKeyword *node) const UNREACHABLE(); } -} // namespace panda::es2panda::compiler \ No newline at end of file +} // namespace panda::es2panda::compiler diff --git a/ets2panda/compiler/core/JSCompiler.cpp b/ets2panda/compiler/core/JSCompiler.cpp index 10a894d364..fd403e50db 100644 --- a/ets2panda/compiler/core/JSCompiler.cpp +++ b/ets2panda/compiler/core/JSCompiler.cpp @@ -582,6 +582,12 @@ void JSCompiler::Compile(const ir::BinaryExpression *expr) const UNREACHABLE(); } +void JSCompiler::Compile(const ir::BlockExpression *expr) const +{ + (void)expr; + UNREACHABLE(); +} + void JSCompiler::Compile(const ir::CallExpression *expr) const { (void)expr; @@ -1263,4 +1269,4 @@ void JSCompiler::Compile(const ir::TSVoidKeyword *node) const UNREACHABLE(); } -} // namespace panda::es2panda::compiler \ No newline at end of file +} // namespace panda::es2panda::compiler diff --git a/ets2panda/compiler/core/compilerContext.h b/ets2panda/compiler/core/compilerContext.h index 420a9fb18b..96e7144d45 100644 --- a/ets2panda/compiler/core/compilerContext.h +++ b/ets2panda/compiler/core/compilerContext.h @@ -16,13 +16,11 @@ #ifndef ES2PANDA_COMPILER_CORE_COMPILER_CONTEXT_H #define ES2PANDA_COMPILER_CORE_COMPILER_CONTEXT_H -#include "macros.h" -#include "mem/arena_allocator.h" +#include + #include "es2panda.h" #include "compiler/base/literals.h" - -#include -#include +#include "parser/parserImpl.h" namespace panda::es2panda::varbinder { class VarBinder; @@ -41,7 +39,7 @@ class CodeGen; class ProgramElement; class AstCompiler; -class CompilerContext { +class CompilerContext final { public: using CodeGenCb = std::function; @@ -52,72 +50,83 @@ public: { } + CompilerContext() = delete; NO_COPY_SEMANTIC(CompilerContext); NO_MOVE_SEMANTIC(CompilerContext); ~CompilerContext() = default; - varbinder::VarBinder *VarBinder() const + [[nodiscard]] varbinder::VarBinder *VarBinder() const noexcept { return varbinder_; } - checker::Checker *Checker() const + [[nodiscard]] checker::Checker *Checker() const noexcept { return checker_; } - Emitter *GetEmitter() const + [[nodiscard]] parser::ParserImpl *GetParser() const noexcept + { + return parser_; + } + + void SetParser(parser::ParserImpl *const parser) noexcept + { + parser_ = parser; + } + + [[nodiscard]] Emitter *GetEmitter() const noexcept { return emitter_; } - void SetEmitter(Emitter *emitter) + void SetEmitter(Emitter *emitter) noexcept { emitter_ = emitter; } - const CodeGenCb &GetCodeGenCb() const + [[nodiscard]] const CodeGenCb &GetCodeGenCb() const noexcept { return code_gen_cb_; } - int32_t AddContextLiteral(LiteralBuffer &&literals) + [[nodiscard]] int32_t AddContextLiteral(LiteralBuffer &&literals) { buff_storage_.emplace_back(std::move(literals)); return buff_storage_.size() - 1; } - const std::vector &ContextLiterals() const + [[nodiscard]] const std::vector &ContextLiterals() const noexcept { return buff_storage_; } - const CompilerOptions *Options() const + [[nodiscard]] const CompilerOptions *Options() const noexcept { return &options_; } - bool IsDebug() const + [[nodiscard]] bool IsDebug() const noexcept { return options_.is_debug; } - bool DumpDebugInfo() const + [[nodiscard]] bool DumpDebugInfo() const noexcept { return options_.dump_debug_info; } - bool IsDirectEval() const + [[nodiscard]] bool IsDirectEval() const noexcept { return options_.is_direct_eval; } - bool IsFunctionEval() const + [[nodiscard]] bool IsFunctionEval() const noexcept { return options_.is_function_eval; } - bool IsEval() const + [[nodiscard]] bool IsEval() const noexcept { return options_.is_eval; } @@ -125,6 +134,7 @@ public: private: varbinder::VarBinder *varbinder_; checker::Checker *checker_; + parser::ParserImpl *parser_ = nullptr; Emitter *emitter_ {}; std::vector buff_storage_; CompilerOptions options_; diff --git a/ets2panda/compiler/core/compilerImpl.cpp b/ets2panda/compiler/core/compilerImpl.cpp index 14bc8b7fbd..0d33af9dd5 100644 --- a/ets2panda/compiler/core/compilerImpl.cpp +++ b/ets2panda/compiler/core/compilerImpl.cpp @@ -44,9 +44,6 @@ #include "es2panda.h" #include "util/declgenEts2Ts.h" -#include -#include - namespace panda::es2panda::compiler { void CompilerImpl::HandleContextLiterals(CompilerContext *context) @@ -115,6 +112,8 @@ static pandasm::Program *CreateCompiler(const CompilationUnit &unit, const Phase auto emitter = Emitter(&context); context.SetEmitter(&emitter); + context.SetParser(&parser); + parser.ParseScript(unit.input, unit.options.compilation_mode == CompilationMode::GEN_STD_LIB); for (auto *phase : get_phases()) { diff --git a/ets2panda/compiler/lowering/ets/opAssignment.cpp b/ets2panda/compiler/lowering/ets/opAssignment.cpp index 03ecf30834..b886773d14 100644 --- a/ets2panda/compiler/lowering/ets/opAssignment.cpp +++ b/ets2panda/compiler/lowering/ets/opAssignment.cpp @@ -15,9 +15,6 @@ // // This is a sample lowering, of little value by itself. -// NOTE: gobabr. -// - temporary variables are inserted into the current scope without any accompanying definition -// construction; most likely, a proper AST checker would complain. // // desc: A compound assignment expression of the form E1 op= E2 is equivalent to E1 = // ((E1) op (E2)) as T, where T is the type of E1, except that E1 is evaluated only @@ -25,22 +22,18 @@ // #include "opAssignment.h" -#include "checker/types/typeFlag.h" -#include "varbinder/variableFlags.h" + +#include "parser/ETSparser.h" +#include "varbinder/ETSBinder.h" #include "checker/ETSchecker.h" -#include "compiler/core/compilerContext.h" #include "compiler/lowering/util.h" -#include "ir/astNode.h" -#include "ir/expression.h" #include "ir/opaqueTypeNode.h" #include "ir/expressions/assignmentExpression.h" -#include "ir/expressions/binaryExpression.h" #include "ir/expressions/identifier.h" #include "ir/expressions/memberExpression.h" -#include "ir/expressions/sequenceExpression.h" +#include "ir/expressions/blockExpression.h" #include "ir/statements/blockStatement.h" -#include "ir/ts/tsAsExpression.h" -#include "lexer/token/tokenType.h" +#include "ir/statements/expressionStatement.h" namespace panda::es2panda::compiler { @@ -83,52 +76,6 @@ static lexer::TokenType OpEqualToOp(const lexer::TokenType op_equal) UNREACHABLE(); } -// This should probably be a virtual method of AstNode -static ir::AstNode *CloneNode(checker::ETSChecker *checker, ir::AstNode *ast) -{ - if (ast->IsIdentifier()) { - const auto *id = ast->AsIdentifier(); - auto *res = checker->AllocNode(id->Name(), id->TypeAnnotation(), checker->Allocator()); - res->SetVariable(id->Variable()); - res->SetOptional(id->IsOptional()); - res->SetReference(id->IsReference()); - - if (id->IsTdz()) { - res->SetTdz(); - } - - if (id->IsAccessor()) { - res->SetAccessor(); - } - - if (id->IsMutator()) { - res->SetMutator(); - } - - res->SetPrivate(id->IsPrivate()); - if (id->IsIgnoreBox()) { - res->SetIgnoreBox(); - } - - return res; - } - - ASSERT(ast->IsMemberExpression()); - - auto *me = ast->AsMemberExpression(); - auto *object = CloneNode(checker, me->Object())->AsExpression(); - auto *property = CloneNode(checker, me->Property())->AsExpression(); - - auto *res = - checker->AllocNode(object, property, me->Kind(), me->IsComputed(), me->IsOptional()); - res->SetPropVar(me->PropVar()); - if (me->IsIgnoreBox()) { - res->SetIgnoreBox(); - } - - return res; -} - void AdjustBoxingUnboxingFlags(ir::Expression *new_expr, const ir::Expression *old_expr) { // NOTE: gogabr. make sure that the checker never puts both a boxing and an unboxing flag on the same node. @@ -138,112 +85,128 @@ void AdjustBoxingUnboxingFlags(ir::Expression *new_expr, const ir::Expression *o const ir::BoxingUnboxingFlags old_unboxing_flag {old_expr->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::UNBOXING_FLAG}; - if (new_expr->TsType()->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE) && - old_boxing_flag != ir::BoxingUnboxingFlags::NONE) { + if (new_expr->TsType()->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) { new_expr->SetBoxingUnboxingFlags(old_boxing_flag); - } else if (new_expr->TsType()->IsETSObjectType() && old_unboxing_flag != ir::BoxingUnboxingFlags::NONE) { + } else if (new_expr->TsType()->IsETSObjectType()) { new_expr->SetBoxingUnboxingFlags(old_unboxing_flag); } } -ir::Expression *HandleOpAssignment(checker::ETSChecker *checker, ir::AssignmentExpression *assignment) +ir::Expression *HandleOpAssignment(checker::ETSChecker *checker, parser::ETSParser *parser, + ir::AssignmentExpression *assignment) { if (assignment->TsType() == nullptr) { // hasn't been through checker return assignment; } - checker::SavedCheckerContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY}; + const auto op_equal = assignment->OperatorType(); + ASSERT(op_equal != lexer::TokenType::PUNCTUATOR_SUBSTITUTION); + ASSERT(parser != nullptr); - ir::Expression *tmp_assignment_for_obj = nullptr; - ir::Expression *tmp_assignment_for_prop = nullptr; - ir::Expression *left_adjusted = nullptr; + auto *const allocator = checker->Allocator(); - auto *left = assignment->Left(); - auto *right = assignment->Right(); + auto *const left = assignment->Left(); + auto *const right = assignment->Right(); + auto *const scope = NearestScope(assignment); - const auto op_equal = assignment->OperatorType(); - ASSERT(op_equal != lexer::TokenType::PUNCTUATOR_SUBSTITUTION); + std::string new_assignment_statements {}; - if (left->IsIdentifier() || (left->IsMemberExpression() && left->AsMemberExpression()->Object()->IsIdentifier() && - left->AsMemberExpression()->Property()->IsIdentifier())) { - left_adjusted = left->AsExpression(); - } else { - ASSERT(left->IsMemberExpression()); - - auto *left_memb = left->AsMemberExpression(); - auto *scope = NearestScope(assignment); - - auto *tmp_obj_var_id = Gensym(checker->Allocator()); - auto *tmp_obj_var = scope->AddDecl( - checker->Allocator(), tmp_obj_var_id->Name(), varbinder::VariableFlags::LOCAL); - tmp_obj_var->SetTsType(left_memb->Object()->TsType()); - tmp_obj_var_id->SetVariable(tmp_obj_var); - tmp_obj_var_id->SetTsType(tmp_obj_var->TsType()); - tmp_assignment_for_obj = checker->AllocNode( - tmp_obj_var_id, left_memb->Object(), lexer::TokenType::PUNCTUATOR_SUBSTITUTION); - - tmp_assignment_for_obj->SetTsType(tmp_obj_var->TsType()); - - auto *property = left_memb->Property(); - if (!property->IsIdentifier()) { - auto *tmp_prop_var_id = Gensym(checker->Allocator()); - auto *tmp_prop_var = scope->AddDecl( - checker->Allocator(), tmp_prop_var_id->Name(), varbinder::VariableFlags::LOCAL); - tmp_prop_var->SetTsType(left_memb->Property()->TsType()); - tmp_prop_var_id->SetVariable(tmp_prop_var); - tmp_prop_var_id->SetTsType(tmp_prop_var->TsType()); - - tmp_assignment_for_prop = checker->AllocNode( - tmp_prop_var_id, left_memb->Property(), lexer::TokenType::PUNCTUATOR_SUBSTITUTION); - tmp_assignment_for_prop->SetTsType(tmp_prop_var->TsType()); - property = tmp_prop_var_id; - } + ir::Identifier *ident1; + ir::Identifier *ident2 = nullptr; + ir::Expression *object = nullptr; + ir::Expression *property = nullptr; - left_adjusted = checker->AllocNode(tmp_obj_var_id, property, left_memb->Kind(), - left_memb->IsComputed(), left_memb->IsOptional()); - left_adjusted->AsMemberExpression()->SetPropVar(left_memb->PropVar()); - left_adjusted->AsMemberExpression()->SetObjectType(left_memb->ObjType()); - left_adjusted->SetTsType(left->TsType()); + checker::SavedCheckerContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY}; - if (left_memb->IsIgnoreBox()) { - left_adjusted->AsMemberExpression()->SetIgnoreBox(); + // Create temporary variable(s) if left hand of assignment is not defined by simple identifier[s] + if (left->IsIdentifier()) { + ident1 = left->AsIdentifier(); + } else if (left->IsMemberExpression()) { + auto *const member_expression = left->AsMemberExpression(); + + if (object = member_expression->Object(); object->IsIdentifier()) { + ident1 = object->AsIdentifier(); + } else { + ident1 = Gensym(allocator); + new_assignment_statements = "let @@I1 = (@@E2); "; } - } - left_adjusted->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::NONE); // to be recomputed - auto *left_cloned = CloneNode(checker, left_adjusted)->AsExpression(); - auto *new_right = checker->AllocNode(left_cloned, right, OpEqualToOp(op_equal)); - - auto *lc_type = left_cloned->Check(checker); + if (property = member_expression->Property(); property->IsIdentifier()) { + ident2 = property->AsIdentifier(); + } else { + ident2 = Gensym(allocator); + new_assignment_statements += "let @@I3 = (@@E4); "; + } + } else { + UNREACHABLE(); + } + // Create proxy TypeNode for left hand of assignment expression + auto *lc_type = left->TsType(); if (auto *lc_type_as_primitive = checker->ETSBuiltinTypeAsPrimitiveType(lc_type); lc_type_as_primitive != nullptr) { lc_type = lc_type_as_primitive; } - - auto *lc_type_node = checker->AllocNode(lc_type); - auto *new_right_converted = checker->AllocNode(new_right, lc_type_node, false); - auto *new_assignment = checker->AllocNode(left_adjusted, new_right_converted, - lexer::TokenType::PUNCTUATOR_SUBSTITUTION); - - ir::Expression *res = new_assignment; - if (tmp_assignment_for_obj != nullptr) { - ArenaVector seq_exprs {checker->Allocator()->Adapter()}; - seq_exprs.push_back(tmp_assignment_for_obj); - - if (tmp_assignment_for_prop != nullptr) { - seq_exprs.push_back(tmp_assignment_for_prop); + auto *expr_type = checker->AllocNode(lc_type); + + // Generate ArkTS code string for new lowered assignment expression: + std::string left_hand = "@@I5"; + std::string right_hand = "@@I7"; + + if (ident2 != nullptr) { + if (auto const kind = left->AsMemberExpression()->Kind(); kind == ir::MemberExpressionKind::PROPERTY_ACCESS) { + left_hand += ".@@I6"; + right_hand += ".@@I8"; + } else if (kind == ir::MemberExpressionKind::ELEMENT_ACCESS) { + left_hand += "[@@I6]"; + right_hand += "[@@I8]"; + } else { + UNREACHABLE(); } + } - seq_exprs.push_back(new_assignment); - auto *seq = checker->AllocNode(std::move(seq_exprs)); - res = seq; + new_assignment_statements += left_hand + " = (" + right_hand + ' ' + + std::string {lexer::TokenToString(OpEqualToOp(op_equal))} + " (@@E9)) as @@T10"; + // std::cout << "Lowering statements: " << new_assignment_statements << std::endl; + + // Parse ArkTS code string and create and process corresponding AST node(s) + auto expression_ctx = varbinder::LexicalScope::Enter(checker->VarBinder(), scope); + + auto *lowering_result = parser->CreateFormattedExpression( + new_assignment_statements, parser::DEFAULT_SOURCE_FILE, ident1, object, ident2, property, + ident1->Clone(allocator), ident2 != nullptr ? ident2->Clone(allocator) : nullptr, ident1->Clone(allocator), + ident2 != nullptr ? ident2->Clone(allocator) : nullptr, right, expr_type); + lowering_result->SetParent(assignment->Parent()); + + checker->VarBinder()->AsETSBinder()->ResolveReferencesForScope(lowering_result, scope); + lowering_result->Check(checker); + + // Adjust [un]boxing flag + ir::AssignmentExpression *new_assignment; + if (lowering_result->IsAssignmentExpression()) { + new_assignment = lowering_result->AsAssignmentExpression(); + } else if (lowering_result->IsBlockExpression() && !lowering_result->AsBlockExpression()->Statements().empty() && + lowering_result->AsBlockExpression()->Statements().back()->IsExpressionStatement() && + lowering_result->AsBlockExpression() + ->Statements() + .back() + ->AsExpressionStatement() + ->GetExpression() + ->IsAssignmentExpression()) { + new_assignment = lowering_result->AsBlockExpression() + ->Statements() + .back() + ->AsExpressionStatement() + ->GetExpression() + ->AsAssignmentExpression(); + } else { + UNREACHABLE(); } - res->SetParent(assignment->Parent()); - new_assignment->Check(checker); + // NOTE(gogabr): make sure that the checker never puts both a boxing and an unboxing flag on the same node. + // Then this code will become unnecessary. AdjustBoxingUnboxingFlags(new_assignment, assignment); - return res; + return lowering_result; } bool OpAssignmentLowering::Perform(CompilerContext *ctx, parser::Program *program) @@ -257,12 +220,13 @@ bool OpAssignmentLowering::Perform(CompilerContext *ctx, parser::Program *progra } } - checker::ETSChecker *checker = ctx->Checker()->AsETSChecker(); + auto *const checker = ctx->Checker()->AsETSChecker(); + auto *const parser = ctx->GetParser()->AsETSParser(); - program->Ast()->TransformChildrenRecursively([checker](ir::AstNode *ast) -> ir::AstNode * { + program->Ast()->TransformChildrenRecursively([checker, parser](ir::AstNode *ast) -> ir::AstNode * { if (ast->IsAssignmentExpression() && ast->AsAssignmentExpression()->OperatorType() != lexer::TokenType::PUNCTUATOR_SUBSTITUTION) { - return HandleOpAssignment(checker, ast->AsAssignmentExpression()); + return HandleOpAssignment(checker, parser, ast->AsAssignmentExpression()); } return ast; diff --git a/ets2panda/compiler/lowering/util.cpp b/ets2panda/compiler/lowering/util.cpp index 247f182093..ed4d02de65 100644 --- a/ets2panda/compiler/lowering/util.cpp +++ b/ets2panda/compiler/lowering/util.cpp @@ -31,17 +31,18 @@ varbinder::Scope *NearestScope(const ir::AstNode *ast) return ast->Scope(); } -static size_t GENSYM_COUNTER = 0; +ir::Identifier *Gensym(ArenaAllocator *const allocator) +{ + util::UString const s = GenName(allocator); + return allocator->New(s.View(), allocator); +} -ir::Identifier *Gensym(ArenaAllocator *allocator) +util::UString GenName(ArenaAllocator *const allocator) { - std::stringstream ss; - ss << "gensym$" << (GENSYM_COUNTER++); - const ArenaString s {allocator->Adapter()}; - const auto str = ss.str(); - auto *arena_pointer = allocator->Alloc(str.size() + 1); - std::memmove(arena_pointer, reinterpret_cast(str.c_str()), str.size() + 1); - return allocator->New(util::StringView(reinterpret_cast(arena_pointer)), allocator); + static std::string const GENSYM_CORE = "gensym$_"; + static std::size_t gensym_counter = 0U; + + return util::UString {GENSYM_CORE + std::to_string(++gensym_counter), allocator}; } } // namespace panda::es2panda::compiler diff --git a/ets2panda/compiler/lowering/util.h b/ets2panda/compiler/lowering/util.h index ba3d290a13..d8344157b1 100644 --- a/ets2panda/compiler/lowering/util.h +++ b/ets2panda/compiler/lowering/util.h @@ -16,14 +16,13 @@ #ifndef ES2PANDA_COMPILER_LOWERING_UTIL_H #define ES2PANDA_COMPILER_LOWERING_UTIL_H -#include "varbinder/scope.h" -#include "checker/types/ets/etsObjectType.h" #include "ir/astNode.h" namespace panda::es2panda::compiler { varbinder::Scope *NearestScope(const ir::AstNode *ast); ir::Identifier *Gensym(ArenaAllocator *allocator); +util::UString GenName(ArenaAllocator *allocator); } // namespace panda::es2panda::compiler diff --git a/ets2panda/ir/astNodeMapping.h b/ets2panda/ir/astNodeMapping.h index 9b8099c600..e05e9a9c2f 100644 --- a/ets2panda/ir/astNodeMapping.h +++ b/ets2panda/ir/astNodeMapping.h @@ -160,7 +160,8 @@ _(VARIABLE_DECLARATOR, VariableDeclarator) \ _(WHILE_STATEMENT, WhileStatement) \ _(YIELD_EXPRESSION, YieldExpression) \ - _(OPAQUE_TYPE_NODE, OpaqueTypeNode) + _(OPAQUE_TYPE_NODE, OpaqueTypeNode) \ + _(BLOCK_EXPRESSION, BlockExpression) #define AST_NODE_REINTERPRET_MAPPING(_) \ _(ARRAY_EXPRESSION, ARRAY_PATTERN, ArrayExpression, ArrayPattern) \ diff --git a/ets2panda/ir/base/property.cpp b/ets2panda/ir/base/property.cpp index f607be57b1..93641aea37 100644 --- a/ets2panda/ir/base/property.cpp +++ b/ets2panda/ir/base/property.cpp @@ -26,7 +26,9 @@ #include "ir/validationInfo.h" namespace panda::es2panda::ir { -Property::Property([[maybe_unused]] Tag const tag, Expression *const key, Expression *const value) : Property(*this) +Property::Property([[maybe_unused]] Tag const tag, Property const &other, Expression *const key, + Expression *const value) + : Property(other) { key_ = key; value_ = value; @@ -38,7 +40,7 @@ Expression *Property::Clone(ArenaAllocator *const allocator, AstNode *const pare auto *const key = key_ != nullptr ? key_->Clone(allocator) : nullptr; auto *const value = value_ != nullptr ? value_->Clone(allocator) : nullptr; - if (auto *const clone = allocator->New(Tag {}, key, value); clone != nullptr) { + if (auto *const clone = allocator->New(Tag {}, *this, key, value); clone != nullptr) { if (key != nullptr) { key->SetParent(clone); } diff --git a/ets2panda/ir/base/property.h b/ets2panda/ir/base/property.h index 91d91512c4..7d8d3c5a28 100644 --- a/ets2panda/ir/base/property.h +++ b/ets2panda/ir/base/property.h @@ -50,7 +50,7 @@ public: { } - explicit Property(Tag tag, Expression *key, Expression *value); + explicit Property(Tag tag, Property const &other, Expression *key, Expression *value); [[nodiscard]] Expression *Key() noexcept { diff --git a/ets2panda/ir/expressions/blockExpression.cpp b/ets2panda/ir/expressions/blockExpression.cpp new file mode 100644 index 0000000000..02e3ff477e --- /dev/null +++ b/ets2panda/ir/expressions/blockExpression.cpp @@ -0,0 +1,101 @@ +/** + * 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. + */ + +#include "blockExpression.h" + +#include "ir/astDump.h" +#include "compiler/core/ETSGen.h" +#include "checker/ETSchecker.h" +#include "ir/astNode.h" + +namespace panda::es2panda::ir { + +BlockExpression::BlockExpression(ArenaVector &&statements) + : Expression(AstNodeType::BLOCK_EXPRESSION), statements_(std::move(statements)) +{ + for (auto *const node : statements_) { + node->SetParent(this); + } +} + +BlockExpression::BlockExpression([[maybe_unused]] Tag const tag, BlockExpression const &other, + ArenaAllocator *const allocator) + : Expression(static_cast(other)), statements_(allocator->Adapter()) +{ + for (auto *const node : other.statements_) { + statements_.emplace_back(node->Clone(allocator, this)->AsStatement()); + } +} + +// NOLINTNEXTLINE(google-default-arguments) +BlockExpression *BlockExpression::Clone(ArenaAllocator *const allocator, AstNode *const parent) +{ + if (auto *const clone = allocator->New(Tag {}, *this, allocator); clone != nullptr) { + if (parent != nullptr) { + clone->SetParent(parent); + } + return clone; + } + throw Error(ErrorType::GENERIC, "", CLONE_ALLOCATION_ERROR); +} + +void BlockExpression::TransformChildren(const NodeTransformer &cb) +{ + for (auto *&node : statements_) { + node = cb(node)->AsStatement(); + } +} + +void BlockExpression::Iterate(const NodeTraverser &cb) const +{ + for (auto *const node : statements_) { + cb(node); + } +} + +void BlockExpression::Dump(ir::AstDumper *dumper) const +{ + dumper->Add({{"type", "BlockExpression"}, {"statements", statements_}}); +} + +void BlockExpression::Compile([[maybe_unused]] compiler::PandaGen *pg) const +{ + UNREACHABLE(); +} + +void BlockExpression::Compile(compiler::ETSGen *etsg) const +{ + for (auto const *const node : statements_) { + node->Compile(etsg); + } +} + +checker::Type *BlockExpression::Check([[maybe_unused]] checker::TSChecker *checker) +{ + UNREACHABLE(); +} + +checker::Type *BlockExpression::Check(checker::ETSChecker *checker) +{ + if (TsType() == nullptr) { + for (auto *const node : statements_) { + if (auto *const expr_type = node->Check(checker); expr_type != nullptr) { + SetTsType(expr_type); + } + } + } + return TsType(); +} +} // namespace panda::es2panda::ir diff --git a/ets2panda/ir/expressions/blockExpression.h b/ets2panda/ir/expressions/blockExpression.h new file mode 100644 index 0000000000..ef4f0b8b58 --- /dev/null +++ b/ets2panda/ir/expressions/blockExpression.h @@ -0,0 +1,60 @@ +/** + * 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. + */ + +#ifndef ES2PANDA_IR_EXPRESSION_BLOCK_EXPRESSION_H +#define ES2PANDA_IR_EXPRESSION_BLOCK_EXPRESSION_H + +#include "ir/astNode.h" +#include "ir/expression.h" +#include "ir/statement.h" + +namespace panda::es2panda::ir { + +class BlockExpression : public Expression { +private: + struct Tag {}; + +public: + BlockExpression() = delete; + ~BlockExpression() override = default; + + NO_COPY_SEMANTIC(BlockExpression); + NO_MOVE_SEMANTIC(BlockExpression); + + explicit BlockExpression(ArenaVector &&statements); + explicit BlockExpression(Tag tag, BlockExpression const &other, ArenaAllocator *allocator); + + [[nodiscard]] ArenaVector const &Statements() const noexcept + { + return statements_; + } + + // NOLINTNEXTLINE(google-default-arguments) + [[nodiscard]] BlockExpression *Clone(ArenaAllocator *allocator, AstNode *parent = nullptr) override; + + void TransformChildren(const NodeTransformer &cb) override; + void Iterate(const NodeTraverser &cb) const override; + void Dump(ir::AstDumper *dumper) const override; + void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; + void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; + checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; + checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; + +private: + ArenaVector statements_; +}; +} // namespace panda::es2panda::ir + +#endif diff --git a/ets2panda/ir/expressions/memberExpression.cpp b/ets2panda/ir/expressions/memberExpression.cpp index 44936411de..8801cd4589 100644 --- a/ets2panda/ir/expressions/memberExpression.cpp +++ b/ets2panda/ir/expressions/memberExpression.cpp @@ -35,8 +35,9 @@ #include "util/helpers.h" namespace panda::es2panda::ir { -MemberExpression::MemberExpression([[maybe_unused]] Tag const tag, Expression *const object, Expression *const property) - : MemberExpression(*this) +MemberExpression::MemberExpression([[maybe_unused]] Tag const tag, MemberExpression const &other, + Expression *const object, Expression *const property) + : MemberExpression(other) { object_ = object; if (object_ != nullptr) { @@ -474,7 +475,7 @@ Expression *MemberExpression::Clone(ArenaAllocator *const allocator, AstNode *co auto *const object = object_ != nullptr ? object_->Clone(allocator) : nullptr; auto *const property = property_ != nullptr ? property_->Clone(allocator) : nullptr; - if (auto *const clone = allocator->New(Tag {}, object, property); clone != nullptr) { + if (auto *const clone = allocator->New(Tag {}, *this, object, property); clone != nullptr) { if (parent != nullptr) { clone->SetParent(parent); } diff --git a/ets2panda/ir/expressions/memberExpression.h b/ets2panda/ir/expressions/memberExpression.h index 47c172e843..2206f553d7 100644 --- a/ets2panda/ir/expressions/memberExpression.h +++ b/ets2panda/ir/expressions/memberExpression.h @@ -57,7 +57,7 @@ public: { } - explicit MemberExpression(Tag tag, Expression *object, Expression *property); + explicit MemberExpression(Tag tag, MemberExpression const &other, Expression *object, Expression *property); [[nodiscard]] Expression *Object() noexcept { diff --git a/ets2panda/lexer/lexer.cpp b/ets2panda/lexer/lexer.cpp index 0b26a83a57..dba2f94d63 100644 --- a/ets2panda/lexer/lexer.cpp +++ b/ets2panda/lexer/lexer.cpp @@ -779,6 +779,16 @@ void Lexer::ScanAmpersandPunctuator() } } +void Lexer::ScanAtPunctuator() +{ + GetToken().type_ = TokenType::PUNCTUATOR_AT; + + if (Iterator().Peek() == LEX_CHAR_AT) { + GetToken().type_ = TokenType::PUNCTUATOR_FORMAT; + Iterator().Forward(1U); + } +} + void Lexer::ScanVLinePunctuator() { GetToken().type_ = TokenType::PUNCTUATOR_BITWISE_OR; @@ -1360,7 +1370,7 @@ void Lexer::NextToken(Keywords *kws) break; } case LEX_CHAR_AT: { - GetToken().type_ = TokenType::PUNCTUATOR_AT; + ScanAtPunctuator(); break; } case LEX_CHAR_DOLLAR_SIGN: { diff --git a/ets2panda/lexer/lexer.h b/ets2panda/lexer/lexer.h index 2a36059a2b..4f8cd73a1c 100644 --- a/ets2panda/lexer/lexer.h +++ b/ets2panda/lexer/lexer.h @@ -230,6 +230,7 @@ protected: void ScanDotPunctuator(); void ScanColonPunctuator(); virtual bool ScanDollarPunctuator(); + void ScanAtPunctuator(); virtual void SkipMultiLineComment(); virtual void ScanHashMark(); diff --git a/ets2panda/lexer/token/token.cpp b/ets2panda/lexer/token/token.cpp index a6bb09dfb0..885adaa546 100644 --- a/ets2panda/lexer/token/token.cpp +++ b/ets2panda/lexer/token/token.cpp @@ -226,6 +226,8 @@ const char *TokenToString(TokenType type) // NOLINT(readability-function-size) return "?."; case TokenType::PUNCTUATOR_AT: return "@"; + case TokenType::PUNCTUATOR_FORMAT: + return "@@"; case TokenType::PUNCTUATOR_RIGHT_PARENTHESIS: return ")"; case TokenType::PUNCTUATOR_LEFT_PARENTHESIS: diff --git a/ets2panda/lexer/token/tokenType.h b/ets2panda/lexer/token/tokenType.h index 7872cd90d7..5f6b4df393 100644 --- a/ets2panda/lexer/token/tokenType.h +++ b/ets2panda/lexer/token/tokenType.h @@ -94,6 +94,7 @@ enum class TokenType { PUNCTUATOR_BACK_TICK, PUNCTUATOR_HASH_MARK, PUNCTUATOR_AT, + PUNCTUATOR_FORMAT, PUNCTUATOR_DOLLAR_DOLLAR, /* contextual keywords */ diff --git a/ets2panda/parser/ETSparser.cpp b/ets2panda/parser/ETSparser.cpp index e86b81930b..f99fff53a1 100644 --- a/ets2panda/parser/ETSparser.cpp +++ b/ets2panda/parser/ETSparser.cpp @@ -14,6 +14,7 @@ */ #include "ETSparser.h" +#include #include "parser/parserFlags.h" #include "util/arktsconfig.h" @@ -46,6 +47,7 @@ #include "ir/expressions/assignmentExpression.h" #include "ir/expressions/sequenceExpression.h" #include "ir/expressions/callExpression.h" +#include "ir/expressions/blockExpression.h" #include "ir/expressions/thisExpression.h" #include "ir/expressions/superExpression.h" #include "ir/expressions/newExpression.h" @@ -2616,10 +2618,10 @@ ir::TypeNode *ETSParser::ParseFunctionType() return func_type; } -ir::TypeNode *ETSParser::ParseTypeAnnotation(TypeAnnotationParsingOptions *options) +// Just to reduce the size of ParseTypeAnnotation(...) method +std::pair ETSParser::GetTypeAnnotationFromToken(TypeAnnotationParsingOptions *options) { ir::TypeNode *type_annotation = nullptr; - bool throw_error = ((*options) & TypeAnnotationParsingOptions::THROW_ERROR) != 0; switch (Lexer()->GetToken().Type()) { case lexer::TokenType::LITERAL_IDENT: { @@ -2632,7 +2634,7 @@ ir::TypeNode *ETSParser::ParseTypeAnnotation(TypeAnnotationParsingOptions *optio if (((*options) & TypeAnnotationParsingOptions::POTENTIAL_CLASS_LITERAL) != 0 && (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_CLASS || IsStructKeyword())) { - return type_annotation; + return std::make_pair(type_annotation, false); } break; } @@ -2678,14 +2680,14 @@ ir::TypeNode *ETSParser::ParseTypeAnnotation(TypeAnnotationParsingOptions *optio Lexer()->Lookahead() == lexer::LEX_CHAR_COLON)) { type_annotation = ParseFunctionType(); type_annotation->SetStart(start_loc); - return type_annotation; + return std::make_pair(type_annotation, false); } type_annotation = ParseTypeAnnotation(options); type_annotation->SetStart(start_loc); if (Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS) { - if (throw_error) { + if (((*options) & TypeAnnotationParsingOptions::THROW_ERROR) != 0) { ThrowExpectedToken(lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS); } @@ -2697,25 +2699,44 @@ ir::TypeNode *ETSParser::ParseTypeAnnotation(TypeAnnotationParsingOptions *optio break; } + case lexer::TokenType::PUNCTUATOR_FORMAT: { + type_annotation = ParseTypeFormatPlaceholder(); + break; + } default: { break; } } + return std::make_pair(type_annotation, true); +} + +ir::TypeNode *ETSParser::ParseTypeAnnotation(TypeAnnotationParsingOptions *options) +{ + bool const throw_error = ((*options) & TypeAnnotationParsingOptions::THROW_ERROR) != 0; + + auto [type_annotation, need_further_processing] = GetTypeAnnotationFromToken(options); + if (type_annotation == nullptr) { if (throw_error) { ThrowSyntaxError("Invalid Type"); } - return nullptr; } + if (!need_further_processing) { + return type_annotation; + } + const lexer::SourcePosition &start_pos = Lexer()->GetToken().Start(); if (((*options) & TypeAnnotationParsingOptions::ALLOW_INTERSECTION) != 0 && Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_BITWISE_AND) { if (type_annotation->IsETSPrimitiveType()) { - ThrowSyntaxError("Invalid intersection type."); + if (throw_error) { + ThrowSyntaxError("Invalid intersection type."); + } + return nullptr; } return ParseIntersectionType(type_annotation); @@ -2728,7 +2749,6 @@ ir::TypeNode *ETSParser::ParseTypeAnnotation(TypeAnnotationParsingOptions *optio if (throw_error) { ThrowExpectedToken(lexer::TokenType::PUNCTUATOR_RIGHT_SQUARE_BRACKET); } - return nullptr; } @@ -3653,6 +3673,9 @@ ir::Expression *ETSParser::ParsePrimaryExpression(ExpressionParseFlags flags) case lexer::TokenType::KEYW_TYPE: { ThrowSyntaxError("Type alias is allowed only as top-level declaration"); } + case lexer::TokenType::PUNCTUATOR_FORMAT: { + return ParseExpressionFormatPlaceholder(); + } default: { return ParseDefaultPrimaryExpression(flags); } @@ -3847,6 +3870,9 @@ ir::Expression *ETSParser::ParsePostPrimaryExpression(ir::Expression *primary_ex continue; } + case lexer::TokenType::PUNCTUATOR_FORMAT: { + ThrowUnexpectedToken(lexer::TokenType::PUNCTUATOR_FORMAT); + } default: { break; } @@ -3861,11 +3887,12 @@ ir::Expression *ETSParser::ParsePostPrimaryExpression(ir::Expression *primary_ex ir::Expression *ETSParser::ParsePotentialAsExpression(ir::Expression *primary_expr) { ASSERT(Lexer()->GetToken().Type() == lexer::TokenType::KEYW_AS); + Lexer()->NextToken(); TypeAnnotationParsingOptions options = TypeAnnotationParsingOptions::THROW_ERROR | TypeAnnotationParsingOptions::ALLOW_INTERSECTION; - Lexer()->NextToken(); ir::TypeNode *type = ParseTypeAnnotation(&options); + auto *as_expression = AllocNode(primary_expr, type, false); as_expression->SetRange(primary_expr->Range()); return as_expression; @@ -4468,6 +4495,148 @@ void ETSParser::CheckDeclare() // Methods to create AST node(s) from the specified string (part of valid ETS-code!) //================================================================================================// +// NOLINTBEGIN(modernize-avoid-c-arrays) +static constexpr char const INVALID_NUMBER_NODE[] = "Invalid node number in format expression."; +static constexpr char const INVALID_FORMAT_NODE[] = "Invalid node type in format expression."; +static constexpr char const INSERT_NODE_ABSENT[] = "There is no any node to insert at the placeholder position."; +static constexpr char const INVALID_INSERT_NODE[] = + "Inserting node type differs from that required by format specification."; +// NOLINTEND(modernize-avoid-c-arrays) + +ParserImpl::NodeFormatType ETSParser::GetFormatPlaceholderIdent() const +{ + ASSERT(Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_FORMAT); + Lexer()->NextToken(); + ASSERT(Lexer()->GetToken().Type() == lexer::TokenType::LITERAL_IDENT); + + char const *const ident_data = Lexer()->GetToken().Ident().Bytes(); + + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic, cert-err34-c) + auto ident_number = std::atoi(ident_data + 1U); + if (ident_number <= 0) { + ThrowSyntaxError(INVALID_NUMBER_NODE, Lexer()->GetToken().Start()); + } + + return {*ident_data, static_cast().second)>(ident_number - 1)}; +} + +ir::AstNode *ETSParser::ParseFormatPlaceholder() +{ + if (inserting_nodes_.empty()) { + ThrowSyntaxError(INSERT_NODE_ABSENT, Lexer()->GetToken().Start()); + } + + if (auto node_format = GetFormatPlaceholderIdent(); node_format.first == EXPRESSION_FORMAT_NODE) { + return ParseExpressionFormatPlaceholder(std::make_optional(node_format)); + } else if (node_format.first == IDENTIFIER_FORMAT_NODE) { // NOLINT(readability-else-after-return) + return ParseIdentifierFormatPlaceholder(std::make_optional(node_format)); + } else if (node_format.first == TYPE_FORMAT_NODE) { // NOLINT(readability-else-after-return) + return ParseTypeFormatPlaceholder(std::make_optional(node_format)); + } else if (node_format.first == STATEMENT_FORMAT_NODE) { // NOLINT(readability-else-after-return) + return ParseStatementFormatPlaceholder(std::make_optional(node_format)); + } + + ThrowSyntaxError(INVALID_FORMAT_NODE, Lexer()->GetToken().Start()); +} + +ir::Expression *ETSParser::ParseExpressionFormatPlaceholder(std::optional node_format) +{ + if (!node_format.has_value()) { + if (inserting_nodes_.empty()) { + ThrowSyntaxError(INSERT_NODE_ABSENT, Lexer()->GetToken().Start()); + } + + if (node_format = GetFormatPlaceholderIdent(); node_format->first == TYPE_FORMAT_NODE) { + return ParseTypeFormatPlaceholder(std::move(node_format)); + } else if (node_format->first == IDENTIFIER_FORMAT_NODE) { // NOLINT(readability-else-after-return) + return ParseIdentifierFormatPlaceholder(std::move(node_format)); + } else if (node_format->first != EXPRESSION_FORMAT_NODE) { // NOLINT(readability-else-after-return) + ThrowSyntaxError(INVALID_FORMAT_NODE, Lexer()->GetToken().Start()); + } + } + + auto *const inserting_node = + node_format->second < inserting_nodes_.size() ? inserting_nodes_[node_format->second] : nullptr; + if (inserting_node == nullptr || !inserting_node->IsExpression()) { + ThrowSyntaxError(INVALID_INSERT_NODE, Lexer()->GetToken().Start()); + } + + auto *const insert_expression = inserting_node->AsExpression(); + Lexer()->NextToken(); + return insert_expression; +} + +ir::TypeNode *ETSParser::ParseTypeFormatPlaceholder(std::optional node_format) +{ + if (!node_format.has_value()) { + if (inserting_nodes_.empty()) { + ThrowSyntaxError(INSERT_NODE_ABSENT, Lexer()->GetToken().Start()); + } + + if (node_format = GetFormatPlaceholderIdent(); node_format->first != TYPE_FORMAT_NODE) { + ThrowSyntaxError(INVALID_FORMAT_NODE, Lexer()->GetToken().Start()); + } + } + + auto *const inserting_node = + node_format->second < inserting_nodes_.size() ? inserting_nodes_[node_format->second] : nullptr; + if (inserting_node == nullptr || !inserting_node->IsExpression() || !inserting_node->AsExpression()->IsTypeNode()) { + ThrowSyntaxError(INVALID_INSERT_NODE, Lexer()->GetToken().Start()); + } + + auto *const insert_type = inserting_node->AsExpression()->AsTypeNode(); + Lexer()->NextToken(); + return insert_type; +} + +// NOLINTNEXTLINE(google-default-arguments) +ir::Identifier *ETSParser::ParseIdentifierFormatPlaceholder(std::optional node_format) +{ + if (!node_format.has_value()) { + if (inserting_nodes_.empty()) { + ThrowSyntaxError(INSERT_NODE_ABSENT, Lexer()->GetToken().Start()); + } + + if (node_format = GetFormatPlaceholderIdent(); node_format->first != IDENTIFIER_FORMAT_NODE) { + ThrowSyntaxError(INVALID_FORMAT_NODE, Lexer()->GetToken().Start()); + } + } + + auto *const inserting_node = + node_format->second < inserting_nodes_.size() ? inserting_nodes_[node_format->second] : nullptr; + if (inserting_node == nullptr || !inserting_node->IsExpression() || + !inserting_node->AsExpression()->IsIdentifier()) { + ThrowSyntaxError(INVALID_INSERT_NODE, Lexer()->GetToken().Start()); + } + + auto *const insert_identifier = inserting_node->AsExpression()->AsIdentifier(); + Lexer()->NextToken(); + return insert_identifier; +} + +ir::Statement *ETSParser::ParseStatementFormatPlaceholder(std::optional node_format) +{ + if (!node_format.has_value()) { + if (inserting_nodes_.empty()) { + ThrowSyntaxError(INSERT_NODE_ABSENT, Lexer()->GetToken().Start()); + } + + if (node_format = GetFormatPlaceholderIdent(); node_format->first != STATEMENT_FORMAT_NODE) { + ThrowSyntaxError(INVALID_FORMAT_NODE, Lexer()->GetToken().Start()); + } + } + + auto *const inserting_node = + node_format->second < inserting_nodes_.size() ? inserting_nodes_[node_format->second] : nullptr; + if (inserting_node == nullptr || !inserting_node->IsStatement()) { + ThrowSyntaxError(INVALID_INSERT_NODE, Lexer()->GetToken().Start()); + } + + auto *const insert_statement = inserting_node->AsStatement(); + Lexer()->NextToken(); + return insert_statement; +} + ir::Statement *ETSParser::CreateStatement(std::string_view const source_code, std::string_view const file_name) { util::UString source {source_code, Allocator()}; @@ -4498,6 +4667,16 @@ ir::Statement *ETSParser::CreateStatement(std::string_view const source_code, st return block_stmt; } +ir::Statement *ETSParser::CreateFormattedStatement(std::string_view const source_code, + std::vector &inserting_nodes, + std::string_view const file_name) +{ + inserting_nodes_.swap(inserting_nodes); + auto const statement = CreateStatement(source_code, file_name); + inserting_nodes_.swap(inserting_nodes); + return statement; +} + ArenaVector ETSParser::CreateStatements(std::string_view const source_code, std::string_view const file_name) { @@ -4509,6 +4688,16 @@ ArenaVector ETSParser::CreateStatements(std::string_view const return ParseStatementList(StatementParsingFlags::STMT_GLOBAL_LEXICAL); } +ArenaVector ETSParser::CreateFormattedStatements(std::string_view const source_code, + std::vector &inserting_nodes, + std::string_view const file_name) +{ + inserting_nodes_.swap(inserting_nodes); + auto statements = CreateStatements(source_code, file_name); + inserting_nodes_.swap(inserting_nodes); + return statements; +} + ir::MethodDefinition *ETSParser::CreateMethodDefinition(ir::ModifierFlags modifiers, std::string_view const source_code, std::string_view const file_name) { @@ -4535,15 +4724,38 @@ ir::MethodDefinition *ETSParser::CreateMethodDefinition(ir::ModifierFlags modifi return method_definition; } -ir::Expression *ETSParser::CreateExpression(ExpressionParseFlags const flags, std::string_view const source_code, +ir::Expression *ETSParser::CreateExpression(std::string_view const source_code, ExpressionParseFlags const flags, std::string_view const file_name) { util::UString source {source_code, Allocator()}; auto const isp = InnerSourceParser(this); auto const lexer = InitLexer({file_name, source.View().Utf8()}); + lexer::SourcePosition const start_loc = lexer->GetToken().Start(); lexer->NextToken(); - return ParseExpression(flags); + + ir::Expression *return_expression = ParseExpression(flags); + return_expression->SetRange({start_loc, lexer->GetToken().End()}); + + return return_expression; +} + +ir::Expression *ETSParser::CreateFormattedExpression(std::string_view const source_code, + std::vector &inserting_nodes, + std::string_view const file_name) +{ + ir::Expression *return_expression; + inserting_nodes_.swap(inserting_nodes); + + if (auto statements = CreateStatements(source_code, file_name); + statements.size() == 1U && statements.back()->IsExpressionStatement()) { + return_expression = statements.back()->AsExpressionStatement()->GetExpression(); + } else { + return_expression = AllocNode(std::move(statements)); + } + + inserting_nodes_.swap(inserting_nodes); + return return_expression; } ir::TypeNode *ETSParser::CreateTypeAnnotation(TypeAnnotationParsingOptions *options, std::string_view const source_code, diff --git a/ets2panda/parser/ETSparser.h b/ets2panda/parser/ETSparser.h index ba8e78e359..fd66dd0a76 100644 --- a/ets2panda/parser/ETSparser.h +++ b/ets2panda/parser/ETSparser.h @@ -16,6 +16,7 @@ #ifndef ES2PANDA_PARSER_CORE_ETS_PARSER_H #define ES2PANDA_PARSER_CORE_ETS_PARSER_H +#include #include "util/arktsconfig.h" #include "TypedParser.h" #include "ir/ets/etsParameterExpression.h" @@ -27,6 +28,16 @@ enum class PrimitiveType; } // namespace panda::es2panda::ir namespace panda::es2panda::parser { + +// NOLINTBEGIN(modernize-avoid-c-arrays) +inline constexpr char const FORMAT_SIGNATURE = '@'; +inline constexpr char const TYPE_FORMAT_NODE = 'T'; +inline constexpr char const STATEMENT_FORMAT_NODE = 'S'; +inline constexpr char const EXPRESSION_FORMAT_NODE = 'E'; +inline constexpr char const IDENTIFIER_FORMAT_NODE = 'I'; +inline constexpr char const DEFAULT_SOURCE_FILE[] = ".ets"; +// NOLINTEND(modernize-avoid-c-arrays) + class ETSParser final : public TypedParser { public: ETSParser(Program *program, const CompilerOptions &options, ParserStatus status = ParserStatus::NO_OPTS) @@ -34,14 +45,53 @@ public: { } + ETSParser() = delete; NO_COPY_SEMANTIC(ETSParser); NO_MOVE_SEMANTIC(ETSParser); - ~ETSParser() = default; - ir::Expression *CreateExpression(ExpressionParseFlags flags, std::string_view source_code, + [[nodiscard]] bool IsETSParser() const noexcept override + { + return true; + } + + // Methods to create AST node(s) from the specified string (part of valid ETS-code!) + // NOTE: the correct initial scope should be entered BEFORE calling any of these methods, + // and correct parent and, probably, variable set to the node(s) after obtaining + + ir::Expression *CreateExpression(std::string_view source_code, + ExpressionParseFlags flags = ExpressionParseFlags::NO_OPTS, std::string_view file_name = DEFAULT_SOURCE_FILE); + ir::Expression *CreateFormattedExpression(std::string_view source_code, std::vector &inserting_nodes, + std::string_view file_name = DEFAULT_SOURCE_FILE); + + template + ir::Expression *CreateFormattedExpression(std::string_view const source_code, std::string_view const file_name, + Args &&...args) + { + std::vector inserting_nodes {}; + inserting_nodes.reserve(sizeof...(Args)); + (inserting_nodes.emplace_back(std::forward(args)), ...); + return CreateFormattedExpression(source_code, inserting_nodes, file_name); + } + + ArenaVector CreateStatements(std::string_view source_code, + std::string_view file_name = DEFAULT_SOURCE_FILE); + + ArenaVector CreateFormattedStatements(std::string_view source_code, + std::vector &inserting_nodes, + std::string_view file_name = DEFAULT_SOURCE_FILE); + + template + ArenaVector CreateFormattedStatements(std::string_view const source_code, + std::string_view const file_name, Args &&...args) + { + std::vector inserting_nodes {}; + (inserting_nodes.emplace(std::forward(args)), ...); + return CreateFormattedStatements(source_code, inserting_nodes, file_name); + } + private: struct ImportData { Language lang; @@ -126,6 +176,7 @@ private: ir::ArrowFunctionExpression *ParseArrowFunctionExpression(); void ThrowIfVarDeclaration(VariableParsingFlags flags) override; + std::pair GetTypeAnnotationFromToken(TypeAnnotationParsingOptions *options); ir::TypeNode *ParseTypeAnnotation(TypeAnnotationParsingOptions *options) override; ir::TSTypeAliasDeclaration *ParseTypeAliasDeclaration() override; @@ -179,6 +230,14 @@ private: ir::Expression *ParseAwaitExpression(); ir::TSTypeParameter *ParseTypeParameter(TypeAnnotationParsingOptions *options) override; + NodeFormatType GetFormatPlaceholderIdent() const; + ir::AstNode *ParseFormatPlaceholder(); + ir::Statement *ParseStatementFormatPlaceholder(std::optional node_format = std::nullopt); + ir::Expression *ParseExpressionFormatPlaceholder(std::optional node_format = std::nullopt); + // NOLINTNEXTLINE(google-default-arguments) + ir::Identifier *ParseIdentifierFormatPlaceholder(std::optional node_format = std::nullopt) override; + ir::TypeNode *ParseTypeFormatPlaceholder(std::optional node_format = std::nullopt); + ir::TSEnumDeclaration *ParseEnumMembers(ir::Identifier *key, const lexer::SourcePosition &enum_start, bool is_const, bool is_static) override; void ParseNumberEnum(ArenaVector &members); @@ -246,17 +305,25 @@ private: // Methods to create AST node(s) from the specified string (part of valid ETS-code!) // NOTE: the correct initial scope should be entered BEFORE calling any of these methods, // and correct parent and, probably, variable set to the node(s) after obtaining - // NOLINTNEXTLINE(modernize-avoid-c-arrays) - inline static constexpr char const DEFAULT_SOURCE_FILE[] = ".ets"; - // NOLINTBEGIN(google-default-arguments) + ir::Statement *CreateStatement(std::string_view source_code, std::string_view file_name = DEFAULT_SOURCE_FILE); - ArenaVector CreateStatements(std::string_view source_code, - std::string_view file_name = DEFAULT_SOURCE_FILE); + ir::Statement *CreateFormattedStatement(std::string_view source_code, std::vector &inserting_nodes, + std::string_view file_name = DEFAULT_SOURCE_FILE); + + template + ir::Statement *CreateFormattedStatement(std::string_view const source_code, std::string_view const file_name, + Args &&...args) + { + std::vector inserting_nodes {}; + (inserting_nodes.emplace(std::forward(args)), ...); + return CreateFormattedStatement(source_code, inserting_nodes, file_name); + } + ir::MethodDefinition *CreateMethodDefinition(ir::ModifierFlags modifiers, std::string_view source_code, std::string_view file_name = DEFAULT_SOURCE_FILE); + ir::TypeNode *CreateTypeAnnotation(TypeAnnotationParsingOptions *options, std::string_view source_code, std::string_view file_name = DEFAULT_SOURCE_FILE); - // NOLINTEND(google-default-arguments) friend class ExternalSourceParser; friend class InnerSourceParser; @@ -264,6 +331,7 @@ private: private: parser::Program *global_program_; std::vector parsed_sources_; + std::vector inserting_nodes_ {}; }; class ExternalSourceParser { diff --git a/ets2panda/parser/TypedParser.cpp b/ets2panda/parser/TypedParser.cpp index b2955aec9c..9167b18ade 100644 --- a/ets2panda/parser/TypedParser.cpp +++ b/ets2panda/parser/TypedParser.cpp @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021 - 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 @@ -1274,6 +1274,8 @@ ir::Expression *TypedParser::ParseQualifiedReference(ir::Expression *type_name, Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_MULTIPLY) { Lexer()->NextToken(); // eat '*' prop_name = AllocNode(varbinder::VarBinder::STAR_IMPORT, Allocator()); + } else if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_FORMAT) { + prop_name = ParseIdentifierFormatPlaceholder(); } else if (Lexer()->GetToken().Type() != lexer::TokenType::LITERAL_IDENT) { if ((flags & ExpressionParseFlags::POTENTIAL_CLASS_LITERAL) != 0) { if (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_CLASS) { diff --git a/ets2panda/parser/parserImpl.cpp b/ets2panda/parser/parserImpl.cpp index 13ef8c6729..4a19f88427 100644 --- a/ets2panda/parser/parserImpl.cpp +++ b/ets2panda/parser/parserImpl.cpp @@ -1137,6 +1137,10 @@ void ParserImpl::ThrowParameterModifierError(ir::ModifierFlags status) const ir::Identifier *ParserImpl::ExpectIdentifier(bool is_reference) { + if (lexer_->GetToken().Type() == lexer::TokenType::PUNCTUATOR_FORMAT) { + return ParseIdentifierFormatPlaceholder(); + } + if (lexer_->GetToken().Type() != lexer::TokenType::LITERAL_IDENT) { ThrowSyntaxError("Identifier expected."); } diff --git a/ets2panda/parser/parserImpl.h b/ets2panda/parser/parserImpl.h index 21c674b2e6..5d0c578d0e 100644 --- a/ets2panda/parser/parserImpl.h +++ b/ets2panda/parser/parserImpl.h @@ -1,5 +1,5 @@ /** - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021 - 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 @@ -101,6 +101,8 @@ enum class CatchClauseType; namespace panda::es2panda::parser { +class ETSParser; + using FunctionSignature = std::tuple, ir::TypeNode *, varbinder::FunctionParamScope *, panda::es2panda::ir::ScriptFunctionFlags>; @@ -181,6 +183,23 @@ public: ScriptExtension Extension() const; + [[nodiscard]] virtual bool IsETSParser() const noexcept + { + return false; + } + + ETSParser *AsETSParser() + { + ASSERT(IsETSParser()); + return reinterpret_cast(this); + } + + const ETSParser *AsETSParser() const + { + ASSERT(IsETSParser()); + return reinterpret_cast(this); + } + protected: virtual void ParseProgram(ScriptKind kind); static ExpressionParseFlags CarryExpressionParserFlag(ExpressionParseFlags origin, ExpressionParseFlags carry); @@ -502,6 +521,14 @@ protected: return ir::ScriptFunctionFlags::NONE; } + using NodeFormatType = std::pair; + // NOLINTNEXTLINE(google-default-arguments) + virtual ir::Identifier *ParseIdentifierFormatPlaceholder( + [[maybe_unused]] std::optional node_format = std::nullopt) + { + ThrowSyntaxError("Identifier expected"); + } + virtual std::tuple ParseFunctionBody( const ArenaVector ¶ms, ParserStatus new_status, ParserStatus context_status, varbinder::FunctionScope *func_scope); diff --git a/ets2panda/public/es2panda_lib.cpp b/ets2panda/public/es2panda_lib.cpp index 6b6c6e5cf4..6a19d98166 100644 --- a/ets2panda/public/es2panda_lib.cpp +++ b/ets2panda/public/es2panda_lib.cpp @@ -132,6 +132,7 @@ static es2panda_Context *CreateContext(es2panda_Config *config, std::string cons varbinder->SetCompilerContext(res->compiler_context.get()); res->emitter = std::make_unique(res->compiler_context.get()); res->compiler_context->SetEmitter(res->emitter.get()); + res->compiler_context->SetParser(res->parser.get()); res->program = nullptr; res->state = ES2PANDA_STATE_NEW; } catch (Error &e) { diff --git a/ets2panda/test/runtime/ets/assignment_lowering.ets b/ets2panda/test/runtime/ets/assignment_lowering.ets new file mode 100644 index 0000000000..75bbd8afd9 --- /dev/null +++ b/ets2panda/test/runtime/ets/assignment_lowering.ets @@ -0,0 +1,43 @@ +/* + * 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. + */ + +class B { + c: int = 11; + d: int = 33; + public constructor() { + this.c += 22; + this.d -= 44; + } +} + +function main(): void { + + let b = new B(); + assert (b.c == 33); + assert (b.d == -11); + + let x: int[] = [1, 2, 3]; + + for (let i: int = 0; i < 3; ++i) { + i <<= 2; + + x[i >> 2] *= 2; + i >>= 2; + } + + for (let i: int = 0; i < 3; ++i) { + assert (x[i] == (i + 1) * 2); + } +} diff --git a/ets2panda/varbinder/ETSBinder.cpp b/ets2panda/varbinder/ETSBinder.cpp index cd271713f1..d12f33d16e 100644 --- a/ets2panda/varbinder/ETSBinder.cpp +++ b/ets2panda/varbinder/ETSBinder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021 - 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 @@ -133,6 +133,40 @@ void ETSBinder::LookupTypeReference(ir::Identifier *ident, bool allow_dynamic_na ThrowUnresolvableType(ident->Start(), name); } +void ETSBinder::ResolveReferencesForScope(ir::AstNode const *const parent, Scope *const scope) +{ + parent->Iterate([this, scope](auto *node) { ResolveReferenceForScope(node, scope); }); +} + +void ETSBinder::ResolveReferenceForScope(ir::AstNode *const node, Scope *const scope) +{ + switch (node->Type()) { + case ir::AstNodeType::IDENTIFIER: { + auto *ident = node->AsIdentifier(); + if (auto const res = scope->Find(ident->Name(), ResolveBindingOptions::ALL); res.variable != nullptr) { + ident->SetVariable(res.variable); + } + break; + } + case ir::AstNodeType::VARIABLE_DECLARATOR: { + auto scope_ctx = LexicalScope::Enter(this, scope); + BuildVarDeclarator(node->AsVariableDeclarator()); + break; + } + /* Maybe will be used + case ir::AstNodeType::BLOCK_STATEMENT: { + auto scope_ctx = LexicalScope::Enter(this, node->AsBlockStatement()->Scope()); + ResolveReferences(node); + break; + } + */ + default: { + ResolveReferencesForScope(node, scope); + break; + } + } +} + void ETSBinder::LookupIdentReference(ir::Identifier *ident) { const auto &name = ident->Name(); diff --git a/ets2panda/varbinder/ETSBinder.h b/ets2panda/varbinder/ETSBinder.h index 32b7feae10..82f49cd9ae 100644 --- a/ets2panda/varbinder/ETSBinder.h +++ b/ets2panda/varbinder/ETSBinder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021 - 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 @@ -198,6 +198,9 @@ import * from "std/interop/js"; import * from "escompat"; )"; + void ResolveReferenceForScope(ir::AstNode *node, Scope *scope); + void ResolveReferencesForScope(ir::AstNode const *parent, Scope *scope); + private: void BuildClassDefinitionImpl(ir::ClassDefinition *class_def); void InitImplicitThisParam(); -- Gitee