diff --git a/ets2panda/BUILD.gn b/ets2panda/BUILD.gn index fc8adbcf0c9180036939111174c800ce1c91b4ec..4fc566a73fed38a50090c8ca16782ba9fd626d56 100644 --- a/ets2panda/BUILD.gn +++ b/ets2panda/BUILD.gn @@ -221,6 +221,7 @@ libes2panda_sources = [ "compiler/lowering/ets/arrayLiteralLowering.cpp", "compiler/lowering/ets/asyncMethodLowering.cpp", "compiler/lowering/ets/bigintLowering.cpp", + "compiler/lowering/ets/binaryExpressionLowering.cpp", "compiler/lowering/ets/boxingForLocals.cpp", "compiler/lowering/ets/capturedVariables.cpp", "compiler/lowering/ets/cfgBuilderPhase.cpp", diff --git a/ets2panda/CMakeLists.txt b/ets2panda/CMakeLists.txt index 9ea3c3f73aa6b4fa696d2c0ad6d614db618e817c..7ed8394a709c8754b73c9f3fff7249f980ff16d8 100644 --- a/ets2panda/CMakeLists.txt +++ b/ets2panda/CMakeLists.txt @@ -301,6 +301,7 @@ set(ES2PANDA_LIB_SRC compiler/lowering/ets/ambientLowering.cpp compiler/lowering/ets/asyncMethodLowering.cpp compiler/lowering/ets/bigintLowering.cpp + compiler/lowering/ets/binaryExpressionLowering.cpp compiler/lowering/ets/recordLowering.cpp compiler/lowering/ets/resizableArrayLowering.cpp compiler/lowering/ets/restArgsLowering.cpp diff --git a/ets2panda/checker/ets/arithmetic.cpp b/ets2panda/checker/ets/arithmetic.cpp index 0415b8a2ed995975f24f4a9b63ce0343b3302612..2ec1c6c06d6cb106ee6cbd7600bab69dd37455ce 100644 --- a/ets2panda/checker/ets/arithmetic.cpp +++ b/ets2panda/checker/ets/arithmetic.cpp @@ -934,6 +934,8 @@ std::map &GetCheckMap() {lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL, &ETSChecker::CheckBinaryOperatorMulDivMod}, {lexer::TokenType::PUNCTUATOR_MOD, &ETSChecker::CheckBinaryOperatorMulDivMod}, {lexer::TokenType::PUNCTUATOR_MOD_EQUAL, &ETSChecker::CheckBinaryOperatorMulDivMod}, + {lexer::TokenType::PUNCTUATOR_EXPONENTIATION, &ETSChecker::CheckBinaryOperatorMulDivMod}, + {lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL, &ETSChecker::CheckBinaryOperatorMulDivMod}, {lexer::TokenType::PUNCTUATOR_MINUS, &ETSChecker::CheckBinaryOperatorPlus}, {lexer::TokenType::PUNCTUATOR_MINUS_EQUAL, &ETSChecker::CheckBinaryOperatorPlus}, diff --git a/ets2panda/compiler/lowering/ets/binaryExpressionLowering.cpp b/ets2panda/compiler/lowering/ets/binaryExpressionLowering.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4662d8a75e519852d0ef84bd30fc52107e802c26 --- /dev/null +++ b/ets2panda/compiler/lowering/ets/binaryExpressionLowering.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025 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 + +#include "binaryExpressionLowering.h" +#include "compiler/lowering/util.h" + +namespace ark::es2panda::compiler { + +using AstNodePtr = ir::AstNode *; + +static ir::AstNode *ConvertExponentiation(ir::BinaryExpression *binaryExpr, public_lib::Context *ctx) +{ + auto *checker = ctx->GetChecker()->AsETSChecker(); + auto *parser = ctx->parser->AsETSParser(); + auto *varbinder = checker->VarBinder()->AsETSBinder(); + + std::string const str = "Math.pow(@@E1, @@E2)"; + auto *parent = binaryExpr->Parent(); + + ir::Expression *loweringResult = parser->CreateFormattedExpression(str, binaryExpr->Left(), binaryExpr->Right()); + ES2PANDA_ASSERT(loweringResult != nullptr); + + loweringResult->SetParent(parent); + loweringResult->SetRange(binaryExpr->Range()); + + auto *scope = NearestScope(parent); + auto bscope = varbinder::LexicalScope::Enter(varbinder, scope); + CheckLoweredNode(varbinder, checker, loweringResult); + return loweringResult; +} + +bool BinaryExpressionLowering::PerformForModule(public_lib::Context *ctx, parser::Program *program) +{ + program->Ast()->TransformChildrenRecursivelyPostorder( + // CC-OFFNXT(G.FMT.14-CPP) project code style + [ctx](ir::AstNode *ast) -> AstNodePtr { + if (ast->IsBinaryExpression()) { + ir::BinaryExpression *binaryExpr = ast->AsBinaryExpression(); + if (binaryExpr->OperatorType() == lexer::TokenType::PUNCTUATOR_EXPONENTIATION) { + return ConvertExponentiation(binaryExpr, ctx); + } + } + + return ast; + }, + Name()); + + return true; +} +} // namespace ark::es2panda::compiler diff --git a/ets2panda/compiler/lowering/ets/binaryExpressionLowering.h b/ets2panda/compiler/lowering/ets/binaryExpressionLowering.h new file mode 100644 index 0000000000000000000000000000000000000000..482ca185567bdd7f8e9191d8677e24b45ce89f0e --- /dev/null +++ b/ets2panda/compiler/lowering/ets/binaryExpressionLowering.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 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_COMPILER_LOWERING_BINARY_EXPRESSION_LOWERING_H +#define ES2PANDA_COMPILER_LOWERING_BINARY_EXPRESSION_LOWERING_H + +#include "compiler/lowering/phase.h" + +namespace ark::es2panda::compiler { + +class BinaryExpressionLowering : public PhaseForDeclarations { +public: + std::string_view Name() const override + { + return "BinaryExpressionLowering"; + } + + bool PerformForModule(public_lib::Context *ctx, parser::Program *program) override; +}; + +} // namespace ark::es2panda::compiler + +#endif diff --git a/ets2panda/compiler/lowering/ets/constantExpressionLowering.cpp b/ets2panda/compiler/lowering/ets/constantExpressionLowering.cpp index 4cfd12a1786a6de62372d43650fe6fb249706d52..42369428dba2cc8016a360961bd930e6cfdd3202 100644 --- a/ets2panda/compiler/lowering/ets/constantExpressionLowering.cpp +++ b/ets2panda/compiler/lowering/ets/constantExpressionLowering.cpp @@ -558,7 +558,7 @@ static bool IsMultiplicativeExpression(const ir::BinaryExpression *expr) { auto opType = expr->OperatorType(); return opType == lexer::TokenType::PUNCTUATOR_MULTIPLY || opType == lexer::TokenType::PUNCTUATOR_DIVIDE || - opType == lexer::TokenType::PUNCTUATOR_MOD; + opType == lexer::TokenType::PUNCTUATOR_MOD || opType == lexer::TokenType::PUNCTUATOR_EXPONENTIATION; } static bool IsRelationalExpression(const ir::BinaryExpression *expr) @@ -636,6 +636,12 @@ static TargetType PerformMultiplicativeOperation(TargetType leftNum, TargetType return std::fmod(leftNum, rightNum); } } + case lexer::TokenType::PUNCTUATOR_EXPONENTIATION: { + if (leftNum < 0 && !std::is_integral_v) { + LogError(context, diagnostic::EXPONENTIATION_BASE_LESS_ZERO, {}, expr->Start()); + } + return std::pow(leftNum, rightNum); + } default: ES2PANDA_UNREACHABLE(); } diff --git a/ets2panda/compiler/lowering/phase.cpp b/ets2panda/compiler/lowering/phase.cpp index 4dd62cd22f43e0431bce0cac6462659346fa8501..b8b1ab646f4e6fe1fa3ed91ba83b32ee02ca7f09 100644 --- a/ets2panda/compiler/lowering/phase.cpp +++ b/ets2panda/compiler/lowering/phase.cpp @@ -23,6 +23,7 @@ #include "compiler/lowering/ets/ambientLowering.h" #include "compiler/lowering/ets/arrayLiteralLowering.h" #include "compiler/lowering/ets/bigintLowering.h" +#include "compiler/lowering/ets/binaryExpressionLowering.h" #include "compiler/lowering/ets/boxingForLocals.h" #include "compiler/lowering/ets/capturedVariables.h" #include "compiler/lowering/ets/constantExpressionLowering.h" @@ -141,7 +142,8 @@ std::vector GetETSPhaseList() new ArrayLiteralLowering, new BigIntLowering, new OpAssignmentLowering, - new SetterLowering, + new BinaryExpressionLowering, // should be after BigIntLowering and OpAssignmentLowering + new SetterLowering, // must be put before ObjectIndexLowering new LateInitializationConvert, new ExtensionAccessorPhase, new BoxingForLocals, diff --git a/ets2panda/lexer/ETSLexer.cpp b/ets2panda/lexer/ETSLexer.cpp index 58645e09fa9e7ebd9e002ed626567f1db84226bd..09ebb112982d974fccde8a068a795a75092bab36 100644 --- a/ets2panda/lexer/ETSLexer.cpp +++ b/ets2panda/lexer/ETSLexer.cpp @@ -103,22 +103,6 @@ bool ETSLexer::CheckUtf16Compatible(char32_t cp) const return true; } -void ETSLexer::ScanAsteriskPunctuator() -{ - GetToken().type_ = TokenType::PUNCTUATOR_MULTIPLY; - - switch (Iterator().Peek()) { - case LEX_CHAR_EQUALS: { - GetToken().type_ = TokenType::PUNCTUATOR_MULTIPLY_EQUAL; - Iterator().Forward(1); - break; - } - default: { - break; - } - } -} - void ETSLexer::ConvertNumber(NumberFlags const flags) { GetToken().number_ = lexer::Number(GetToken().src_, flags); diff --git a/ets2panda/lexer/ETSLexer.h b/ets2panda/lexer/ETSLexer.h index 5bb869a72f75a56021dc2e22e925ef85b924e77d..6d63d300cb5d2584c94df15af1e891bf83b2ac4c 100644 --- a/ets2panda/lexer/ETSLexer.h +++ b/ets2panda/lexer/ETSLexer.h @@ -36,7 +36,6 @@ public: void NextToken(NextTokenFlags flags = NextTokenFlags::NONE) override; void ScanHashMark() override; bool ScanCharLiteral() override; - void ScanAsteriskPunctuator() override; void ScanNumberLeadingZero(bool const leadingMinus) override { diff --git a/ets2panda/test/ast/compiler/ets/illegal_exponentiation_1.ets b/ets2panda/test/ast/compiler/ets/illegal_exponentiation_1.ets new file mode 100644 index 0000000000000000000000000000000000000000..a8329c7756d674f08a196560055221e9742f9ed4 --- /dev/null +++ b/ets2panda/test/ast/compiler/ets/illegal_exponentiation_1.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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. + */ + +function main() { + let x = -1 ** 1.0; +} + +/* @@? 17:11 Error TypeError: Exponent must be an integer if base is less than 0. */ diff --git a/ets2panda/test/runtime/ets/exponentiation_1.ets b/ets2panda/test/runtime/ets/exponentiation_1.ets new file mode 100644 index 0000000000000000000000000000000000000000..c0d7e508d50b2ffce457b7b52ef7e18d5183e321 --- /dev/null +++ b/ets2panda/test/runtime/ets/exponentiation_1.ets @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 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. + */ + + +function main() { + let x1 = 2; + let y1 = 3; + let z1: number = x1 ** y1; + arktest.assertDoubleEQ(z1, 8, 0.00001); + let z2: number = 3 ** 3; + arktest.assertDoubleEQ(z2, 27, 0.00001); + let z3 = 4.0 ** 3; + arktest.assertDoubleEQ(z3, 64, 0.00001); + let x4 = 5; + let y4 = 3; + arktest.assertDoubleEQ(x4 ** y4, 125, 0.00001); +} diff --git a/ets2panda/test/runtime/ets/exponentiation_2.ets b/ets2panda/test/runtime/ets/exponentiation_2.ets new file mode 100644 index 0000000000000000000000000000000000000000..c0d7e508d50b2ffce457b7b52ef7e18d5183e321 --- /dev/null +++ b/ets2panda/test/runtime/ets/exponentiation_2.ets @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 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. + */ + + +function main() { + let x1 = 2; + let y1 = 3; + let z1: number = x1 ** y1; + arktest.assertDoubleEQ(z1, 8, 0.00001); + let z2: number = 3 ** 3; + arktest.assertDoubleEQ(z2, 27, 0.00001); + let z3 = 4.0 ** 3; + arktest.assertDoubleEQ(z3, 64, 0.00001); + let x4 = 5; + let y4 = 3; + arktest.assertDoubleEQ(x4 ** y4, 125, 0.00001); +} diff --git a/ets2panda/util/diagnostic/semantic.yaml b/ets2panda/util/diagnostic/semantic.yaml index d0d3e5c811cb8f29a687159b1bc880767e729ad2..576d09e901fb42408a08a6e55e4707955352deb8 100644 --- a/ets2panda/util/diagnostic/semantic.yaml +++ b/ets2panda/util/diagnostic/semantic.yaml @@ -475,6 +475,10 @@ semantic: id: 274 message: "Expected at least {} type arguments, but got {}." +- name: EXPONENTIATION_BASE_LESS_ZERO + id: 22843 + message: "Exponent must be an integer if base is less than 0." + - name: EXPORT_INCORRECT id: 361 message: "Incorrect export '{}'"