1 /**
2 * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ETSparser.h"
17 #include "lexer/lexer.h"
18 #include "lexer/keywordsUtil.h"
19 #include "ir/astNode.h"
20 #include "ir/base/methodDefinition.h"
21 #include "ir/ets/etsStructDeclaration.h"
22 #include "ir/statements/classDeclaration.h"
23 #include <ir/base/classProperty.h>
24 #include <ir/expressions/arrowFunctionExpression.h>
25 #include <ir/ets/etsModule.h>
26 #include <ir/statements/functionDeclaration.h>
27 #include <ir/statements/variableDeclaration.h>
28 #include <ir/ts/tsInterfaceDeclaration.h>
29 #include <ir/ts/tsTypeAliasDeclaration.h>
30
31 namespace ark::es2panda::parser {
ExcludeInvalidStart()32 void ETSParser::ExcludeInvalidStart()
33 {
34 auto cp = Lexer()->Lookahead();
35 while (!lexer::KeywordsUtil::IsIdentifierStart(cp) && cp != util::StringView::Iterator::INVALID_CP &&
36 cp != lexer::LEX_CHAR_LEFT_BRACE && cp != lexer::LEX_CHAR_ASTERISK) {
37 Lexer()->ForwardToken(Lexer()->GetToken().Type());
38 cp = Lexer()->Lookahead();
39 }
40 }
41
ParseJsDocInfoItemParam()42 std::string ETSParser::ParseJsDocInfoItemParam()
43 {
44 ExcludeInvalidStart();
45 Lexer()->NextToken();
46 auto token = Lexer()->GetToken();
47 std::string jsDocInfoParamStr {};
48 bool needAppendToken = token.Type() != lexer::TokenType::PUNCTUATOR_LEFT_BRACE;
49 bool needBackwardBlank = lexer::KeywordsUtil::IsIdentifierStart(Lexer()->Lookahead());
50 size_t leftBraceCount = 1;
51 while (token.Type() != lexer::TokenType::EOS && token.Type() != lexer::TokenType::JS_DOC_END) {
52 if (needAppendToken) {
53 jsDocInfoParamStr += token.ToString();
54 }
55
56 if (needBackwardBlank) {
57 jsDocInfoParamStr += " ";
58 }
59
60 auto cp = Lexer()->Lookahead();
61 if (lexer::KeywordsUtil::IsIdentifierStart(cp) || cp == lexer::LEX_CHAR_ASTERISK) {
62 Lexer()->NextToken();
63 token = Lexer()->GetToken();
64 needAppendToken = lexer::KeywordsUtil::IsIdentifierStart(cp);
65 needBackwardBlank = lexer::KeywordsUtil::IsIdentifierStart(Lexer()->Lookahead());
66 continue;
67 }
68
69 if (cp == lexer::LEX_CHAR_RIGHT_BRACE) {
70 --leftBraceCount;
71 if (leftBraceCount == 0) {
72 Lexer()->NextToken();
73 break;
74 }
75 }
76
77 if (cp == lexer::LEX_CHAR_LEFT_BRACE) {
78 ++leftBraceCount;
79 }
80
81 if (cp == util::StringView::Iterator::INVALID_CP) {
82 break;
83 }
84
85 jsDocInfoParamStr += std::string(1, cp);
86 needAppendToken = false;
87 needBackwardBlank = false;
88 Lexer()->ForwardToken(token.Type());
89 }
90
91 return jsDocInfoParamStr;
92 }
93
RegularCommentStr(std::string & str)94 static void RegularCommentStr(std::string &str)
95 {
96 if (str.empty()) {
97 return;
98 }
99
100 auto backChar = static_cast<char32_t>(static_cast<unsigned char>(str.back()));
101 while (backChar == lexer::LEX_CHAR_CR || backChar == lexer::LEX_CHAR_LF || backChar == lexer::LEX_CHAR_ASTERISK ||
102 backChar == lexer::LEX_CHAR_NBSP || backChar == lexer::LEX_CHAR_SP) {
103 str.pop_back();
104 if (str.empty()) {
105 return;
106 }
107 backChar = static_cast<char32_t>(static_cast<unsigned char>(str.back()));
108 }
109 }
110
ParseJsDocInfoItemValue()111 std::tuple<util::StringView, util::StringView> ETSParser::ParseJsDocInfoItemValue()
112 {
113 ExcludeInvalidStart();
114 util::UString jsDocInfoItemCommentStr(Allocator());
115 std::string jsDocInfoParamStr {};
116 if (Lexer()->GetToken().Type() == lexer::TokenType::JS_DOC_END) {
117 util::UString jsDocInfoItemParam {jsDocInfoParamStr, Allocator()};
118 return std::make_tuple(jsDocInfoItemParam.View(), jsDocInfoItemCommentStr.View());
119 }
120
121 const auto startIdx = Lexer()->GetIndex();
122 auto escapeEnd = startIdx;
123 do {
124 const char32_t cp = Lexer()->Lookahead();
125 switch (cp) {
126 case lexer::LEX_CHAR_LEFT_BRACE: {
127 jsDocInfoParamStr = ParseJsDocInfoItemParam();
128 continue;
129 }
130 case util::StringView::Iterator::INVALID_CP: {
131 break;
132 }
133 case lexer::LEX_CHAR_CR:
134 case lexer::LEX_CHAR_LF: {
135 Lexer()->HandleNewlineHelper(&jsDocInfoItemCommentStr, &escapeEnd);
136 continue;
137 }
138 case lexer::LEX_CHAR_AT:
139 case lexer::LEX_CHAR_ASTERISK: {
140 auto saved = Lexer()->Save();
141 Lexer()->NextToken();
142 auto nextType = Lexer()->GetToken().Type();
143 Lexer()->Rewind(saved);
144 if (nextType == lexer::TokenType::JS_DOC_END || nextType == lexer::TokenType::PUNCTUATOR_AT) {
145 break;
146 }
147 [[fallthrough]];
148 }
149 default: {
150 Lexer()->SkipCp();
151 continue;
152 }
153 }
154 Lexer()->FinalizeJsDocInfoHelper(&jsDocInfoItemCommentStr, startIdx, escapeEnd);
155 break;
156 } while (true);
157
158 std::string commentStr = std::string(jsDocInfoItemCommentStr.View());
159 RegularCommentStr(commentStr);
160 util::UString jsDocInfoItemParam {jsDocInfoParamStr, Allocator()};
161 util::UString jsDocInfoCommentStr {commentStr, Allocator()};
162 return std::make_tuple(jsDocInfoItemParam.View(), jsDocInfoCommentStr.View());
163 }
164
ParseJsDocInfo()165 ir::JsDocInfo ETSParser::ParseJsDocInfo()
166 {
167 ir::JsDocInfo jsDocInfo(Allocator()->Adapter());
168 auto tokenType = Lexer()->GetToken().Type();
169 while (tokenType != lexer::TokenType::EOS && tokenType != lexer::TokenType::JS_DOC_END) {
170 if (tokenType != lexer::TokenType::PUNCTUATOR_AT) {
171 auto cp = Lexer()->Lookahead();
172 if (cp == lexer::LEX_CHAR_ASTERISK || cp == lexer::LEX_CHAR_AT) {
173 Lexer()->NextToken();
174 tokenType = Lexer()->GetToken().Type();
175 continue;
176 }
177
178 if (Lexer()->Lookahead() == util::StringView::Iterator::INVALID_CP) {
179 break;
180 }
181
182 Lexer()->ForwardToken(tokenType, 1);
183 continue;
184 }
185 Lexer()->NextToken();
186 auto const &token = Lexer()->GetToken();
187 util::UString jsDocInfoItemKey {token.Ident(), Allocator()};
188 auto [jsDocInfoItemParam, jsDocInfoItemComment] = ParseJsDocInfoItemValue();
189 jsDocInfo.emplace(jsDocInfoItemKey.View(),
190 ir::JsDocRecord(jsDocInfoItemKey.View(), jsDocInfoItemParam, jsDocInfoItemComment));
191 tokenType = Lexer()->GetToken().Type();
192 }
193
194 Lexer()->NextToken();
195 return jsDocInfo;
196 }
197
ParseJsDocInfos()198 ArenaVector<ir::JsDocInfo> ETSParser::ParseJsDocInfos()
199 {
200 ArenaVector<ir::JsDocInfo> result(Allocator()->Adapter());
201 bool hasMoreJsDocInfos = true;
202 while (hasMoreJsDocInfos) {
203 auto jsDocInfo = ParseJsDocInfo();
204 if (!jsDocInfo.empty()) {
205 result.emplace_back(jsDocInfo);
206 }
207
208 if (Lexer()->GetToken().Type() != lexer::TokenType::JS_DOC_START) {
209 hasMoreJsDocInfos = false;
210 }
211 }
212 return result;
213 }
214
ApplyJsDocInfoToNamespace(ir::ETSModule * ns,ArenaVector<ir::JsDocInfo> & jsDocInformation)215 static bool ApplyJsDocInfoToNamespace(ir::ETSModule *ns, ArenaVector<ir::JsDocInfo> &jsDocInformation)
216 {
217 if (ns->IsNamespaceChainLastNode()) {
218 ns->SetJsDocInformation(std::move(jsDocInformation));
219 return true;
220 }
221
222 for (auto *node : ns->Statements()) {
223 if (node->IsETSModule()) {
224 if (ApplyJsDocInfoToNamespace(node->AsETSModule(), jsDocInformation)) {
225 return true;
226 }
227 }
228 }
229 return false;
230 }
231
232 // CC-OFFNXT(huge_method,huge_cyclomatic_complexity,G.FUN.01-CPP) big switch-case, solid logic
ApplyJsDocInfoToSpecificNodeType(ir::AstNode * node,ArenaVector<ir::JsDocInfo> && jsDocInformation)233 void ETSParser::ApplyJsDocInfoToSpecificNodeType(ir::AstNode *node, ArenaVector<ir::JsDocInfo> &&jsDocInformation)
234 {
235 if (jsDocInformation.empty()) {
236 return;
237 }
238
239 switch (node->Type()) {
240 case ir::AstNodeType::METHOD_DEFINITION: {
241 auto *func = node->AsMethodDefinition()->Function();
242 ES2PANDA_ASSERT(func != nullptr);
243 func->SetJsDocInformation(std::move(jsDocInformation));
244 break;
245 }
246 case ir::AstNodeType::CLASS_DECLARATION:
247 node->AsClassDeclaration()->Definition()->SetJsDocInformation(std::move(jsDocInformation));
248 break;
249 case ir::AstNodeType::STRUCT_DECLARATION:
250 node->AsETSStructDeclaration()->Definition()->SetJsDocInformation(std::move(jsDocInformation));
251 break;
252 case ir::AstNodeType::FUNCTION_DECLARATION:
253 node->AsFunctionDeclaration()->SetJsDocInformation(std::move(jsDocInformation));
254 break;
255 case ir::AstNodeType::TS_INTERFACE_DECLARATION:
256 node->AsTSInterfaceDeclaration()->SetJsDocInformation(std::move(jsDocInformation));
257 break;
258 case ir::AstNodeType::CLASS_PROPERTY:
259 node->AsClassProperty()->SetJsDocInformation(std::move(jsDocInformation));
260 break;
261 case ir::AstNodeType::VARIABLE_DECLARATION:
262 node->AsVariableDeclaration()->SetJsDocInformation(std::move(jsDocInformation));
263 break;
264 case ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION:
265 node->AsTSTypeAliasDeclaration()->SetJsDocInformation(std::move(jsDocInformation));
266 break;
267 case ir::AstNodeType::ARROW_FUNCTION_EXPRESSION:
268 node->AsArrowFunctionExpression()->SetJsDocInformation(std::move(jsDocInformation));
269 break;
270 case ir::AstNodeType::ETS_MODULE:
271 ApplyJsDocInfoToNamespace(node->AsETSModule(), jsDocInformation);
272 break;
273 default: {
274 }
275 }
276 }
277 } // namespace ark::es2panda::parser
278