vcode-analysis 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- analyzers/__init__.py +24 -0
- analyzers/architecture.py +510 -0
- analyzers/code_review.py +150 -0
- analyzers/directory.py +867 -0
- analyzers/documentation.py +209 -0
- analyzers/security.py +671 -0
- core/__init__.py +17 -0
- core/analyzer.py +207 -0
- core/config.py +166 -0
- core/git_handler.py +718 -0
- core/llm_client.py +186 -0
- parsers/__init__.py +209 -0
- parsers/c/__init__.py +57 -0
- parsers/c/ast_parser.py +424 -0
- parsers/c/models.py +211 -0
- parsers/c/patterns.py +143 -0
- parsers/c/regex_parser.py +594 -0
- parsers/c_parser.py +275 -0
- parsers/java_parser.py +430 -0
- parsers/javascript_parser.py +587 -0
- parsers/kotlin/__init__.py +61 -0
- parsers/kotlin/ast_parser.py +591 -0
- parsers/kotlin/models.py +274 -0
- parsers/kotlin/patterns.py +146 -0
- parsers/kotlin/regex_parser.py +906 -0
- parsers/kotlin_parser.py +279 -0
- parsers/python_parser.py +429 -0
- parsers/typescript_parser.py +381 -0
- vcode_analysis-0.1.0.dist-info/METADATA +246 -0
- vcode_analysis-0.1.0.dist-info/RECORD +34 -0
- vcode_analysis-0.1.0.dist-info/WHEEL +5 -0
- vcode_analysis-0.1.0.dist-info/entry_points.txt +2 -0
- vcode_analysis-0.1.0.dist-info/licenses/LICENSE +21 -0
- vcode_analysis-0.1.0.dist-info/top_level.txt +3 -0
|
@@ -0,0 +1,906 @@
|
|
|
1
|
+
"""Kotlin 正则解析器
|
|
2
|
+
|
|
3
|
+
使用正则表达式快速解析 Kotlin 代码结构。
|
|
4
|
+
支持 Kotlin 特有语法特性的识别。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from .models import (
|
|
12
|
+
KotlinASTResult,
|
|
13
|
+
KotlinFunctionInfo,
|
|
14
|
+
KotlinPropertyInfo,
|
|
15
|
+
KotlinClassInfo,
|
|
16
|
+
KotlinImportInfo,
|
|
17
|
+
KotlinSealedClassInfo,
|
|
18
|
+
KotlinWhenExpression,
|
|
19
|
+
KotlinCoroutineInfo,
|
|
20
|
+
KotlinFlowOperator,
|
|
21
|
+
KotlinTypeAlias,
|
|
22
|
+
KotlinValueClass,
|
|
23
|
+
KotlinDelegatedProperty,
|
|
24
|
+
KotlinExtensionFunction,
|
|
25
|
+
KotlinExtensionProperty,
|
|
26
|
+
KotlinObjectDeclaration,
|
|
27
|
+
KotlinEnumClassInfo,
|
|
28
|
+
KotlinDataClassInfo,
|
|
29
|
+
TERMINAL_FLOW_OPERATORS,
|
|
30
|
+
STANDARD_DELEGATES,
|
|
31
|
+
)
|
|
32
|
+
from .patterns import KOTLIN_PATTERNS, KOTLIN_ADVANCED_PATTERNS
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class KotlinRegexParser:
|
|
36
|
+
"""Kotlin 正则解析器(快速模式)"""
|
|
37
|
+
|
|
38
|
+
def __init__(self):
|
|
39
|
+
self.patterns = {**KOTLIN_PATTERNS, **KOTLIN_ADVANCED_PATTERNS}
|
|
40
|
+
# 预编译正则表达式
|
|
41
|
+
self._compiled = {}
|
|
42
|
+
for name, pattern in self.patterns.items():
|
|
43
|
+
try:
|
|
44
|
+
self._compiled[name] = re.compile(pattern, re.MULTILINE)
|
|
45
|
+
except re.error:
|
|
46
|
+
pass # 忽略编译错误的模式
|
|
47
|
+
|
|
48
|
+
def parse_file(self, file_path: str) -> KotlinASTResult:
|
|
49
|
+
"""解析 Kotlin 文件"""
|
|
50
|
+
path = Path(file_path)
|
|
51
|
+
|
|
52
|
+
if not path.exists():
|
|
53
|
+
return KotlinASTResult(
|
|
54
|
+
file_path=file_path,
|
|
55
|
+
success=False,
|
|
56
|
+
error=f"文件不存在: {file_path}"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if path.suffix not in (".kt", ".kts"):
|
|
60
|
+
return KotlinASTResult(
|
|
61
|
+
file_path=file_path,
|
|
62
|
+
success=False,
|
|
63
|
+
error=f"不是 Kotlin 文件: {file_path}"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
content = path.read_text(encoding="utf-8")
|
|
68
|
+
return self.parse_code(content, file_path)
|
|
69
|
+
except Exception as e:
|
|
70
|
+
return KotlinASTResult(
|
|
71
|
+
file_path=file_path,
|
|
72
|
+
success=False,
|
|
73
|
+
error=str(e)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def parse_code(self, code: str, file_path: str = "<string>") -> KotlinASTResult:
|
|
77
|
+
"""解析 Kotlin 代码字符串"""
|
|
78
|
+
result = KotlinASTResult(
|
|
79
|
+
file_path=file_path,
|
|
80
|
+
success=True,
|
|
81
|
+
parse_mode="fast",
|
|
82
|
+
total_lines=len(code.splitlines())
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
lines = code.split('\n')
|
|
86
|
+
|
|
87
|
+
# 基础解析
|
|
88
|
+
self._parse_package(code, result)
|
|
89
|
+
self._parse_imports(code, result, lines)
|
|
90
|
+
|
|
91
|
+
# Kotlin 特有特性解析
|
|
92
|
+
self._parse_type_aliases(code, result, lines)
|
|
93
|
+
self._parse_value_classes(code, result, lines)
|
|
94
|
+
self._parse_sealed_classes(code, result, lines)
|
|
95
|
+
self._parse_data_classes(code, result, lines)
|
|
96
|
+
self._parse_enum_classes(code, result, lines)
|
|
97
|
+
self._parse_objects(code, result, lines)
|
|
98
|
+
self._parse_delegated_properties(code, result, lines)
|
|
99
|
+
self._parse_coroutines(code, result, lines)
|
|
100
|
+
self._parse_when_expressions(code, result, lines)
|
|
101
|
+
self._parse_extension_functions(code, result, lines)
|
|
102
|
+
self._parse_extension_properties(code, result, lines)
|
|
103
|
+
|
|
104
|
+
# 顶层函数和属性
|
|
105
|
+
self._parse_top_level(code, result, lines)
|
|
106
|
+
|
|
107
|
+
# 普通类解析
|
|
108
|
+
self._parse_classes(code, result, lines)
|
|
109
|
+
|
|
110
|
+
return result
|
|
111
|
+
|
|
112
|
+
def _parse_package(self, code: str, result: KotlinASTResult):
|
|
113
|
+
"""解析包声明"""
|
|
114
|
+
match = self._compiled.get('package')
|
|
115
|
+
if match:
|
|
116
|
+
m = match.search(code)
|
|
117
|
+
if m:
|
|
118
|
+
result.package = m.group(1)
|
|
119
|
+
|
|
120
|
+
def _parse_imports(self, code: str, result: KotlinASTResult, lines: list):
|
|
121
|
+
"""解析导入语句"""
|
|
122
|
+
pattern = self._compiled.get('import')
|
|
123
|
+
if not pattern:
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
for i, line in enumerate(lines, 1):
|
|
127
|
+
m = pattern.match(line.strip())
|
|
128
|
+
if m:
|
|
129
|
+
result.imports.append(KotlinImportInfo(
|
|
130
|
+
path=m.group(1),
|
|
131
|
+
alias=m.group(2) if len(m.groups()) > 1 else None,
|
|
132
|
+
line=i
|
|
133
|
+
))
|
|
134
|
+
|
|
135
|
+
# ==================== 类型系统解析 ====================
|
|
136
|
+
|
|
137
|
+
def _parse_type_aliases(self, code: str, result: KotlinASTResult, lines: list):
|
|
138
|
+
"""解析类型别名"""
|
|
139
|
+
pattern = self._compiled.get('type_alias')
|
|
140
|
+
if not pattern:
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
for i, line in enumerate(lines, 1):
|
|
144
|
+
m = pattern.search(line.strip())
|
|
145
|
+
if m:
|
|
146
|
+
name = m.group(1)
|
|
147
|
+
type_params = m.group(2).split(',') if m.group(2) else []
|
|
148
|
+
underlying = m.group(3).strip() if m.group(3) else ""
|
|
149
|
+
|
|
150
|
+
result.type_aliases.append(KotlinTypeAlias(
|
|
151
|
+
name=name,
|
|
152
|
+
underlying_type=underlying,
|
|
153
|
+
type_params=type_params,
|
|
154
|
+
line=i
|
|
155
|
+
))
|
|
156
|
+
|
|
157
|
+
def _parse_value_classes(self, code: str, result: KotlinASTResult, lines: list):
|
|
158
|
+
"""解析值类(内联类)"""
|
|
159
|
+
pattern = self._compiled.get('value_class')
|
|
160
|
+
if not pattern:
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
for i, line in enumerate(lines, 1):
|
|
164
|
+
m = pattern.search(line.strip())
|
|
165
|
+
if m:
|
|
166
|
+
name = m.group(1)
|
|
167
|
+
wrapped = m.group(2).strip() if m.group(2) else ""
|
|
168
|
+
|
|
169
|
+
result.value_classes.append(KotlinValueClass(
|
|
170
|
+
name=name,
|
|
171
|
+
wrapped_type=wrapped,
|
|
172
|
+
line=i
|
|
173
|
+
))
|
|
174
|
+
|
|
175
|
+
# ==================== Sealed 类解析 ====================
|
|
176
|
+
|
|
177
|
+
def _parse_sealed_classes(self, code: str, result: KotlinASTResult, lines: list):
|
|
178
|
+
"""解析 Sealed 类"""
|
|
179
|
+
sealed_pattern = self._compiled.get('sealed_class')
|
|
180
|
+
sealed_interface_pattern = self._compiled.get('sealed_interface')
|
|
181
|
+
|
|
182
|
+
for i, line in enumerate(lines, 1):
|
|
183
|
+
stripped = line.strip()
|
|
184
|
+
|
|
185
|
+
# 跳过注释
|
|
186
|
+
if stripped.startswith('//') or stripped.startswith('/*'):
|
|
187
|
+
continue
|
|
188
|
+
|
|
189
|
+
# 检查 sealed class
|
|
190
|
+
m = None
|
|
191
|
+
class_type = "sealed class"
|
|
192
|
+
|
|
193
|
+
if sealed_pattern:
|
|
194
|
+
m = sealed_pattern.search(stripped)
|
|
195
|
+
|
|
196
|
+
if not m and sealed_interface_pattern:
|
|
197
|
+
m = sealed_interface_pattern.search(stripped)
|
|
198
|
+
class_type = "sealed interface"
|
|
199
|
+
|
|
200
|
+
if m:
|
|
201
|
+
name = m.group(1)
|
|
202
|
+
type_params = m.group(2).split(',') if m.group(2) else []
|
|
203
|
+
|
|
204
|
+
# 提取类体
|
|
205
|
+
class_body, end_line = self._extract_block(lines, i)
|
|
206
|
+
|
|
207
|
+
# 解析继承
|
|
208
|
+
extends, implements = self._parse_inheritance(stripped)
|
|
209
|
+
|
|
210
|
+
sealed_info = KotlinSealedClassInfo(
|
|
211
|
+
name=name,
|
|
212
|
+
line_start=i,
|
|
213
|
+
line_end=end_line,
|
|
214
|
+
class_type=class_type,
|
|
215
|
+
type_params=type_params,
|
|
216
|
+
primary_constructor_params=[],
|
|
217
|
+
extends=extends,
|
|
218
|
+
implements=implements,
|
|
219
|
+
modifiers=['sealed'],
|
|
220
|
+
annotations=[],
|
|
221
|
+
functions=[],
|
|
222
|
+
properties=[],
|
|
223
|
+
is_sealed=True,
|
|
224
|
+
permitted_subclasses=[]
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# 解析类体中的成员
|
|
228
|
+
if class_body:
|
|
229
|
+
self._parse_class_members(class_body, sealed_info)
|
|
230
|
+
|
|
231
|
+
result.sealed_classes.append(sealed_info)
|
|
232
|
+
|
|
233
|
+
def _parse_inheritance(self, line: str) -> tuple[Optional[str], list[str]]:
|
|
234
|
+
"""解析继承关系"""
|
|
235
|
+
extends = None
|
|
236
|
+
implements = []
|
|
237
|
+
|
|
238
|
+
# 检查 : 后面的内容
|
|
239
|
+
colon_match = re.search(r':\s*([^{]+?)(?:\s*\{|$)', line)
|
|
240
|
+
if colon_match:
|
|
241
|
+
inherit_str = colon_match.group(1).strip()
|
|
242
|
+
parts = inherit_str.split(',')
|
|
243
|
+
|
|
244
|
+
for part in parts:
|
|
245
|
+
part = part.strip()
|
|
246
|
+
if '(' in part:
|
|
247
|
+
# 有构造函数调用的是继承
|
|
248
|
+
extends = part.split('(')[0].strip()
|
|
249
|
+
elif part:
|
|
250
|
+
implements.append(part)
|
|
251
|
+
|
|
252
|
+
return extends, implements
|
|
253
|
+
|
|
254
|
+
# ==================== 数据类解析 ====================
|
|
255
|
+
|
|
256
|
+
def _parse_data_classes(self, code: str, result: KotlinASTResult, lines: list):
|
|
257
|
+
"""解析数据类"""
|
|
258
|
+
pattern = self._compiled.get('data_class')
|
|
259
|
+
if not pattern:
|
|
260
|
+
return
|
|
261
|
+
|
|
262
|
+
for i, line in enumerate(lines, 1):
|
|
263
|
+
m = pattern.search(line.strip())
|
|
264
|
+
if m:
|
|
265
|
+
name = m.group(1)
|
|
266
|
+
type_params = m.group(2).split(',') if m.group(2) else []
|
|
267
|
+
params_str = m.group(3) if m.group(3) else ""
|
|
268
|
+
|
|
269
|
+
# 解析主构造函数参数
|
|
270
|
+
primary_params = self._parse_params(params_str)
|
|
271
|
+
|
|
272
|
+
# 提取类体
|
|
273
|
+
class_body, end_line = self._extract_block(lines, i)
|
|
274
|
+
|
|
275
|
+
extends, implements = self._parse_inheritance(line)
|
|
276
|
+
|
|
277
|
+
data_info = KotlinDataClassInfo(
|
|
278
|
+
name=name,
|
|
279
|
+
line_start=i,
|
|
280
|
+
line_end=end_line,
|
|
281
|
+
class_type="data class",
|
|
282
|
+
type_params=type_params,
|
|
283
|
+
primary_constructor_params=primary_params,
|
|
284
|
+
extends=extends,
|
|
285
|
+
implements=implements,
|
|
286
|
+
modifiers=['data'],
|
|
287
|
+
annotations=[],
|
|
288
|
+
functions=[],
|
|
289
|
+
properties=[],
|
|
290
|
+
component_functions=[f"component{n+1}" for n in range(len(primary_params))],
|
|
291
|
+
copy_function_params=primary_params.copy()
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
if class_body:
|
|
295
|
+
self._parse_class_members(class_body, data_info)
|
|
296
|
+
|
|
297
|
+
result.data_classes.append(data_info)
|
|
298
|
+
|
|
299
|
+
# ==================== 枚举类解析 ====================
|
|
300
|
+
|
|
301
|
+
def _parse_enum_classes(self, code: str, result: KotlinASTResult, lines: list):
|
|
302
|
+
"""解析枚举类"""
|
|
303
|
+
pattern = self._compiled.get('enum_class')
|
|
304
|
+
if not pattern:
|
|
305
|
+
return
|
|
306
|
+
|
|
307
|
+
for i, line in enumerate(lines, 1):
|
|
308
|
+
m = pattern.search(line.strip())
|
|
309
|
+
if m:
|
|
310
|
+
name = m.group(1)
|
|
311
|
+
# 值类型(如果有)
|
|
312
|
+
value_type = m.group(2).strip() if m.group(2) else None
|
|
313
|
+
|
|
314
|
+
# 提取类体
|
|
315
|
+
class_body, end_line = self._extract_block(lines, i)
|
|
316
|
+
|
|
317
|
+
# 解析枚举条目
|
|
318
|
+
entries = []
|
|
319
|
+
if class_body:
|
|
320
|
+
entries = self._parse_enum_entries(class_body)
|
|
321
|
+
|
|
322
|
+
enum_info = KotlinEnumClassInfo(
|
|
323
|
+
name=name,
|
|
324
|
+
line_start=i,
|
|
325
|
+
line_end=end_line,
|
|
326
|
+
class_type="enum class",
|
|
327
|
+
type_params=[],
|
|
328
|
+
primary_constructor_params=[],
|
|
329
|
+
extends=None,
|
|
330
|
+
implements=[],
|
|
331
|
+
modifiers=['enum'],
|
|
332
|
+
annotations=[],
|
|
333
|
+
functions=[],
|
|
334
|
+
properties=[],
|
|
335
|
+
entries=entries
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
if class_body:
|
|
339
|
+
self._parse_class_members(class_body, enum_info)
|
|
340
|
+
|
|
341
|
+
result.enum_classes.append(enum_info)
|
|
342
|
+
|
|
343
|
+
def _parse_enum_entries(self, class_body: str) -> list[dict]:
|
|
344
|
+
"""解析枚举条目"""
|
|
345
|
+
entries = []
|
|
346
|
+
# 查找枚举条目区域(在 ; 之前或 class body 中)
|
|
347
|
+
entry_pattern = re.compile(r'^\s*(\w+)\s*(?:\(([^)]*)\))?\s*(?:,|(?=\n))', re.MULTILINE)
|
|
348
|
+
|
|
349
|
+
for m in entry_pattern.finditer(class_body):
|
|
350
|
+
name = m.group(1)
|
|
351
|
+
# 跳过关键字
|
|
352
|
+
if name in ('class', 'interface', 'object', 'fun', 'val', 'var'):
|
|
353
|
+
continue
|
|
354
|
+
|
|
355
|
+
params = m.group(2) if m.group(2) else None
|
|
356
|
+
entries.append({
|
|
357
|
+
'name': name,
|
|
358
|
+
'params': params,
|
|
359
|
+
'line': 0 # 简化,不提供精确行号
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
return entries
|
|
363
|
+
|
|
364
|
+
# ==================== Object 解析 ====================
|
|
365
|
+
|
|
366
|
+
def _parse_objects(self, code: str, result: KotlinASTResult, lines: list):
|
|
367
|
+
"""解析 Object 声明"""
|
|
368
|
+
pattern = self._compiled.get('object_declaration')
|
|
369
|
+
companion_pattern = self._compiled.get('companion_object')
|
|
370
|
+
|
|
371
|
+
if not pattern:
|
|
372
|
+
return
|
|
373
|
+
|
|
374
|
+
for i, line in enumerate(lines, 1):
|
|
375
|
+
stripped = line.strip()
|
|
376
|
+
|
|
377
|
+
# 跳过 companion object(在类解析中处理)
|
|
378
|
+
if companion_pattern and companion_pattern.search(stripped):
|
|
379
|
+
continue
|
|
380
|
+
|
|
381
|
+
m = pattern.search(stripped)
|
|
382
|
+
if m:
|
|
383
|
+
name = m.group(1)
|
|
384
|
+
extends_str = m.group(2) if m.group(2) else None
|
|
385
|
+
|
|
386
|
+
extends, implements = self._parse_inheritance(stripped)
|
|
387
|
+
|
|
388
|
+
_, end_line = self._extract_block(lines, i)
|
|
389
|
+
|
|
390
|
+
obj_decl = KotlinObjectDeclaration(
|
|
391
|
+
name=name,
|
|
392
|
+
extends=extends or extends_str,
|
|
393
|
+
implements=implements,
|
|
394
|
+
is_companion=False,
|
|
395
|
+
line_start=i,
|
|
396
|
+
line_end=end_line
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
result.object_declarations.append(obj_decl)
|
|
400
|
+
|
|
401
|
+
# ==================== 委托属性解析 ====================
|
|
402
|
+
|
|
403
|
+
def _parse_delegated_properties(self, code: str, result: KotlinASTResult, lines: list):
|
|
404
|
+
"""解析委托属性"""
|
|
405
|
+
pattern = self._compiled.get('delegated_property')
|
|
406
|
+
if not pattern:
|
|
407
|
+
return
|
|
408
|
+
|
|
409
|
+
for i, line in enumerate(lines, 1):
|
|
410
|
+
m = pattern.search(line.strip())
|
|
411
|
+
if m:
|
|
412
|
+
is_val = m.group(1) == 'val'
|
|
413
|
+
name = m.group(2)
|
|
414
|
+
prop_type = m.group(3) if m.group(3) else "Unknown"
|
|
415
|
+
delegate_expr = m.group(4).strip() if m.group(4) else ""
|
|
416
|
+
|
|
417
|
+
# 判断委托类型
|
|
418
|
+
delegate_type = "custom"
|
|
419
|
+
for std_name in STANDARD_DELEGATES:
|
|
420
|
+
if delegate_expr.startswith(std_name):
|
|
421
|
+
delegate_type = std_name
|
|
422
|
+
break
|
|
423
|
+
|
|
424
|
+
result.delegated_properties.append(KotlinDelegatedProperty(
|
|
425
|
+
name=name,
|
|
426
|
+
delegate_type=delegate_type,
|
|
427
|
+
delegate_expression=delegate_expr,
|
|
428
|
+
is_val=is_val,
|
|
429
|
+
line=i
|
|
430
|
+
))
|
|
431
|
+
|
|
432
|
+
# ==================== 协程解析 ====================
|
|
433
|
+
|
|
434
|
+
def _parse_coroutines(self, code: str, result: KotlinASTResult, lines: list):
|
|
435
|
+
"""解析协程和 Flow"""
|
|
436
|
+
suspend_pattern = self._compiled.get('suspend_function')
|
|
437
|
+
flow_pattern = self._compiled.get('flow_type')
|
|
438
|
+
builder_pattern = self._compiled.get('coroutine_builder')
|
|
439
|
+
operator_pattern = self._compiled.get('flow_operator')
|
|
440
|
+
dispatcher_pattern = self._compiled.get('dispatcher')
|
|
441
|
+
scope_pattern = self._compiled.get('coroutine_scope')
|
|
442
|
+
|
|
443
|
+
# 解析 suspend 函数
|
|
444
|
+
if suspend_pattern:
|
|
445
|
+
for i, line in enumerate(lines, 1):
|
|
446
|
+
m = suspend_pattern.search(line.strip())
|
|
447
|
+
if m:
|
|
448
|
+
name = m.group(1)
|
|
449
|
+
params = m.group(2) if m.group(2) else ""
|
|
450
|
+
return_type = m.group(3).strip() if m.group(3) else None
|
|
451
|
+
|
|
452
|
+
# 检查 Flow 类型
|
|
453
|
+
flow_type = None
|
|
454
|
+
if return_type and flow_pattern:
|
|
455
|
+
flow_m = flow_pattern.search(return_type)
|
|
456
|
+
if flow_m:
|
|
457
|
+
flow_type = f"Flow<{flow_m.group(1)}>"
|
|
458
|
+
|
|
459
|
+
result.coroutines.append(KotlinCoroutineInfo(
|
|
460
|
+
function_name=name,
|
|
461
|
+
is_suspend=True,
|
|
462
|
+
flow_type=flow_type,
|
|
463
|
+
line=i
|
|
464
|
+
))
|
|
465
|
+
|
|
466
|
+
# 解析协程构建器
|
|
467
|
+
if builder_pattern:
|
|
468
|
+
for i, line in enumerate(lines, 1):
|
|
469
|
+
for m in builder_pattern.finditer(line):
|
|
470
|
+
builder_type = m.group(1)
|
|
471
|
+
|
|
472
|
+
# 查找 dispatcher
|
|
473
|
+
dispatcher = None
|
|
474
|
+
if dispatcher_pattern:
|
|
475
|
+
disp_m = dispatcher_pattern.search(line)
|
|
476
|
+
if disp_m:
|
|
477
|
+
dispatcher = disp_m.group(1)
|
|
478
|
+
|
|
479
|
+
result.coroutines.append(KotlinCoroutineInfo(
|
|
480
|
+
function_name="<anonymous>",
|
|
481
|
+
is_suspend=False,
|
|
482
|
+
builder_type=builder_type,
|
|
483
|
+
dispatcher=dispatcher,
|
|
484
|
+
line=i
|
|
485
|
+
))
|
|
486
|
+
|
|
487
|
+
# 解析 Flow 操作符
|
|
488
|
+
if operator_pattern:
|
|
489
|
+
for i, line in enumerate(lines, 1):
|
|
490
|
+
for m in operator_pattern.finditer(line):
|
|
491
|
+
op_name = m.group(1)
|
|
492
|
+
is_terminal = op_name in TERMINAL_FLOW_OPERATORS
|
|
493
|
+
|
|
494
|
+
result.flow_operators.append(KotlinFlowOperator(
|
|
495
|
+
name=op_name,
|
|
496
|
+
is_terminal=is_terminal,
|
|
497
|
+
line=i
|
|
498
|
+
))
|
|
499
|
+
|
|
500
|
+
# ==================== When 表达式解析 ====================
|
|
501
|
+
|
|
502
|
+
def _parse_when_expressions(self, code: str, result: KotlinASTResult, lines: list):
|
|
503
|
+
"""解析 when 表达式"""
|
|
504
|
+
pattern = self._compiled.get('when_expression')
|
|
505
|
+
if not pattern:
|
|
506
|
+
return
|
|
507
|
+
|
|
508
|
+
for i, line in enumerate(lines, 1):
|
|
509
|
+
m = pattern.search(line.strip())
|
|
510
|
+
if m:
|
|
511
|
+
subject_var = m.group(1).strip() if m.group(1) else None
|
|
512
|
+
subject_type = None
|
|
513
|
+
|
|
514
|
+
# 提取 when 体
|
|
515
|
+
when_body, end_line = self._extract_block(lines, i)
|
|
516
|
+
|
|
517
|
+
# 解析分支
|
|
518
|
+
branches = []
|
|
519
|
+
if when_body:
|
|
520
|
+
branches = self._parse_when_branches(when_body)
|
|
521
|
+
|
|
522
|
+
result.when_expressions.append(KotlinWhenExpression(
|
|
523
|
+
subject_type=subject_type,
|
|
524
|
+
subject_variable=subject_var,
|
|
525
|
+
branches=branches,
|
|
526
|
+
is_exhaustive=None, # 需要类型推断
|
|
527
|
+
line_start=i,
|
|
528
|
+
line_end=end_line
|
|
529
|
+
))
|
|
530
|
+
|
|
531
|
+
def _parse_when_branches(self, when_body: str) -> list[dict]:
|
|
532
|
+
"""解析 when 分支"""
|
|
533
|
+
branches = []
|
|
534
|
+
lines = when_body.split('\n')
|
|
535
|
+
|
|
536
|
+
branch_pattern = re.compile(r'^\s*(.+?)\s*->')
|
|
537
|
+
|
|
538
|
+
for line in lines:
|
|
539
|
+
m = branch_pattern.match(line)
|
|
540
|
+
if m:
|
|
541
|
+
pattern_str = m.group(1).strip()
|
|
542
|
+
is_else = pattern_str == 'else'
|
|
543
|
+
|
|
544
|
+
branches.append({
|
|
545
|
+
'pattern': pattern_str,
|
|
546
|
+
'is_else': is_else,
|
|
547
|
+
'line': 0
|
|
548
|
+
})
|
|
549
|
+
|
|
550
|
+
return branches
|
|
551
|
+
|
|
552
|
+
# ==================== 扩展函数/属性解析 ====================
|
|
553
|
+
|
|
554
|
+
def _parse_extension_functions(self, code: str, result: KotlinASTResult, lines: list):
|
|
555
|
+
"""解析扩展函数"""
|
|
556
|
+
pattern = self._compiled.get('extension_function')
|
|
557
|
+
if not pattern:
|
|
558
|
+
return
|
|
559
|
+
|
|
560
|
+
for i, line in enumerate(lines, 1):
|
|
561
|
+
m = pattern.search(line.strip())
|
|
562
|
+
if m:
|
|
563
|
+
receiver = m.group(1)
|
|
564
|
+
name = m.group(2)
|
|
565
|
+
params = m.group(3) if m.group(3) else ""
|
|
566
|
+
return_type = m.group(4).strip() if m.group(4) else None
|
|
567
|
+
|
|
568
|
+
# 检查是否在类内(分发接收者)
|
|
569
|
+
in_class = self._is_inside_class(code, i)
|
|
570
|
+
|
|
571
|
+
result.extension_functions.append(KotlinExtensionFunction(
|
|
572
|
+
name=name,
|
|
573
|
+
line_start=i,
|
|
574
|
+
line_end=i,
|
|
575
|
+
return_type=return_type,
|
|
576
|
+
parameters=self._parse_params(params),
|
|
577
|
+
modifiers=[],
|
|
578
|
+
type_params=[],
|
|
579
|
+
receiver_type=receiver,
|
|
580
|
+
dispatch_receiver=in_class
|
|
581
|
+
))
|
|
582
|
+
|
|
583
|
+
def _parse_extension_properties(self, code: str, result: KotlinASTResult, lines: list):
|
|
584
|
+
"""解析扩展属性"""
|
|
585
|
+
pattern = self._compiled.get('extension_property')
|
|
586
|
+
if not pattern:
|
|
587
|
+
return
|
|
588
|
+
|
|
589
|
+
for i, line in enumerate(lines, 1):
|
|
590
|
+
m = pattern.search(line.strip())
|
|
591
|
+
if m:
|
|
592
|
+
is_val = m.group(1) == 'val'
|
|
593
|
+
receiver = m.group(2)
|
|
594
|
+
name = m.group(3)
|
|
595
|
+
|
|
596
|
+
result.extension_properties.append(KotlinExtensionProperty(
|
|
597
|
+
name=name,
|
|
598
|
+
receiver_type=receiver,
|
|
599
|
+
property_type="Unknown",
|
|
600
|
+
is_val=is_val,
|
|
601
|
+
line=i
|
|
602
|
+
))
|
|
603
|
+
|
|
604
|
+
def _is_inside_class(self, code: str, line_num: int) -> bool:
|
|
605
|
+
"""检查某行是否在类内部"""
|
|
606
|
+
lines = code.split('\n')
|
|
607
|
+
depth = 0
|
|
608
|
+
|
|
609
|
+
for i, line in enumerate(lines, 1):
|
|
610
|
+
if i >= line_num:
|
|
611
|
+
return depth > 0
|
|
612
|
+
|
|
613
|
+
depth += line.count('{') - line.count('}')
|
|
614
|
+
|
|
615
|
+
return False
|
|
616
|
+
|
|
617
|
+
# ==================== 顶层结构解析 ====================
|
|
618
|
+
|
|
619
|
+
def _parse_top_level(self, code: str, result: KotlinASTResult, lines: list):
|
|
620
|
+
"""解析顶层函数和属性"""
|
|
621
|
+
brace_depth = 0
|
|
622
|
+
in_block_comment = False
|
|
623
|
+
|
|
624
|
+
for i, line in enumerate(lines, 1):
|
|
625
|
+
stripped = line.strip()
|
|
626
|
+
|
|
627
|
+
# 处理块注释
|
|
628
|
+
if '/*' in stripped:
|
|
629
|
+
in_block_comment = True
|
|
630
|
+
if '*/' in stripped:
|
|
631
|
+
in_block_comment = False
|
|
632
|
+
continue
|
|
633
|
+
if in_block_comment:
|
|
634
|
+
continue
|
|
635
|
+
|
|
636
|
+
# 跳过行注释
|
|
637
|
+
if stripped.startswith('//'):
|
|
638
|
+
brace_depth += line.count('{') - line.count('}')
|
|
639
|
+
continue
|
|
640
|
+
|
|
641
|
+
# 只处理顶层
|
|
642
|
+
if brace_depth == 0:
|
|
643
|
+
# 解析顶层函数
|
|
644
|
+
if 'fun ' in stripped and 'class ' not in stripped and 'sealed' not in stripped:
|
|
645
|
+
func = self._parse_function_line(stripped, i)
|
|
646
|
+
if func:
|
|
647
|
+
result.functions.append(func)
|
|
648
|
+
|
|
649
|
+
# 解析顶层属性
|
|
650
|
+
elif stripped.startswith('val ') or stripped.startswith('var '):
|
|
651
|
+
prop = self._parse_property_line(stripped, i)
|
|
652
|
+
if prop:
|
|
653
|
+
result.properties.append(prop)
|
|
654
|
+
|
|
655
|
+
# 更新大括号深度
|
|
656
|
+
brace_depth += line.count('{') - line.count('}')
|
|
657
|
+
|
|
658
|
+
def _parse_function_line(self, line: str, line_num: int) -> Optional[KotlinFunctionInfo]:
|
|
659
|
+
"""解析函数行"""
|
|
660
|
+
# 简单提取函数名
|
|
661
|
+
name_match = re.search(r'fun\s+(?:<[^>]+>\s+)?(\w+)\s*\(', line)
|
|
662
|
+
if not name_match:
|
|
663
|
+
return None
|
|
664
|
+
|
|
665
|
+
name = name_match.group(1)
|
|
666
|
+
|
|
667
|
+
# 提取参数
|
|
668
|
+
params_match = re.search(r'fun\s+\w+\s*\(([^)]*)\)', line)
|
|
669
|
+
params = self._parse_params(params_match.group(1)) if params_match else []
|
|
670
|
+
|
|
671
|
+
# 提取返回类型
|
|
672
|
+
return_type = None
|
|
673
|
+
ret_match = re.search(r'\)\s*:\s*([^{=\n]+)', line)
|
|
674
|
+
if ret_match:
|
|
675
|
+
return_type = ret_match.group(1).strip()
|
|
676
|
+
|
|
677
|
+
# 提取修饰符
|
|
678
|
+
modifiers = []
|
|
679
|
+
for mod in ['suspend', 'inline', 'tailrec', 'operator', 'infix', 'public', 'private', 'protected', 'internal', 'open', 'override']:
|
|
680
|
+
if re.search(rf'\b{mod}\b', line.split('fun')[0]):
|
|
681
|
+
modifiers.append(mod)
|
|
682
|
+
|
|
683
|
+
return KotlinFunctionInfo(
|
|
684
|
+
name=name,
|
|
685
|
+
line_start=line_num,
|
|
686
|
+
line_end=line_num,
|
|
687
|
+
return_type=return_type,
|
|
688
|
+
parameters=params,
|
|
689
|
+
modifiers=modifiers,
|
|
690
|
+
type_params=[]
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
def _parse_property_line(self, line: str, line_num: int) -> Optional[KotlinPropertyInfo]:
|
|
694
|
+
"""解析属性行"""
|
|
695
|
+
is_val = line.strip().startswith('val')
|
|
696
|
+
|
|
697
|
+
# 提取属性名
|
|
698
|
+
name_match = re.search(r'(val|var)\s+(\w+)', line)
|
|
699
|
+
if not name_match:
|
|
700
|
+
return None
|
|
701
|
+
|
|
702
|
+
name = name_match.group(2)
|
|
703
|
+
|
|
704
|
+
# 提取类型
|
|
705
|
+
prop_type = "Unknown"
|
|
706
|
+
type_match = re.search(r':\s*([^\s=]+)', line)
|
|
707
|
+
if type_match:
|
|
708
|
+
prop_type = type_match.group(1).strip()
|
|
709
|
+
|
|
710
|
+
# 提取修饰符
|
|
711
|
+
modifiers = []
|
|
712
|
+
for mod in ['public', 'private', 'protected', 'internal', 'lateinit', 'const', 'override']:
|
|
713
|
+
prefix = line.split('val')[0] if 'val' in line else line.split('var')[0]
|
|
714
|
+
if mod in prefix:
|
|
715
|
+
modifiers.append(mod)
|
|
716
|
+
|
|
717
|
+
# 检查是否为委托属性
|
|
718
|
+
is_delegate = ' by ' in line
|
|
719
|
+
|
|
720
|
+
return KotlinPropertyInfo(
|
|
721
|
+
name=name,
|
|
722
|
+
type=prop_type,
|
|
723
|
+
line=line_num,
|
|
724
|
+
is_val=is_val,
|
|
725
|
+
modifiers=modifiers,
|
|
726
|
+
is_delegate=is_delegate
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
# ==================== 类解析 ====================
|
|
730
|
+
|
|
731
|
+
def _parse_classes(self, code: str, result: KotlinASTResult, lines: list):
|
|
732
|
+
"""解析普通类声明"""
|
|
733
|
+
for i, line in enumerate(lines, 1):
|
|
734
|
+
stripped = line.strip()
|
|
735
|
+
|
|
736
|
+
# 跳过已解析的特殊类型
|
|
737
|
+
if any(kw in stripped for kw in ['sealed class', 'sealed interface', 'data class', 'enum class', 'value class']):
|
|
738
|
+
continue
|
|
739
|
+
|
|
740
|
+
# 检查普通类声明
|
|
741
|
+
class_match = self._match_class_declaration(stripped)
|
|
742
|
+
if class_match:
|
|
743
|
+
class_info = self._parse_class_declaration(code, lines, i, class_match)
|
|
744
|
+
if class_info:
|
|
745
|
+
result.classes.append(class_info)
|
|
746
|
+
|
|
747
|
+
def _match_class_declaration(self, line: str) -> Optional[re.Match]:
|
|
748
|
+
"""匹配类声明"""
|
|
749
|
+
patterns = [
|
|
750
|
+
# interface
|
|
751
|
+
r'(?:(public|private|protected|internal)\s+)*'
|
|
752
|
+
r'(interface)\s+(\w+)(?:<([^>]+)>)?',
|
|
753
|
+
# 普通类
|
|
754
|
+
r'(?:(public|private|protected|internal|open|abstract|final|inner)\s+)*'
|
|
755
|
+
r'(class)\s+(\w+)(?:<([^>]+)>)?',
|
|
756
|
+
]
|
|
757
|
+
|
|
758
|
+
for pattern in patterns:
|
|
759
|
+
match = re.match(pattern, line)
|
|
760
|
+
if match:
|
|
761
|
+
return match
|
|
762
|
+
return None
|
|
763
|
+
|
|
764
|
+
def _parse_class_declaration(self, code: str, lines: list, line_num: int, match: re.Match) -> Optional[KotlinClassInfo]:
|
|
765
|
+
"""解析类声明"""
|
|
766
|
+
groups = match.groups()
|
|
767
|
+
|
|
768
|
+
# 提取类类型和名称
|
|
769
|
+
class_type = "class"
|
|
770
|
+
name = "Unknown"
|
|
771
|
+
type_params_str = None
|
|
772
|
+
modifiers = []
|
|
773
|
+
|
|
774
|
+
idx = 0
|
|
775
|
+
# 提取修饰符
|
|
776
|
+
while idx < len(groups) and groups[idx] and groups[idx] in ('public', 'private', 'protected', 'internal', 'open', 'abstract', 'final', 'inner'):
|
|
777
|
+
modifiers.append(groups[idx])
|
|
778
|
+
idx += 1
|
|
779
|
+
|
|
780
|
+
# 类类型
|
|
781
|
+
if idx < len(groups) and groups[idx]:
|
|
782
|
+
class_type = groups[idx]
|
|
783
|
+
idx += 1
|
|
784
|
+
|
|
785
|
+
# 类名
|
|
786
|
+
if idx < len(groups) and groups[idx]:
|
|
787
|
+
name = groups[idx]
|
|
788
|
+
idx += 1
|
|
789
|
+
|
|
790
|
+
# 泛型参数
|
|
791
|
+
if idx < len(groups) and groups[idx]:
|
|
792
|
+
type_params_str = groups[idx]
|
|
793
|
+
|
|
794
|
+
type_params = [p.strip() for p in type_params_str.split(',')] if type_params_str else []
|
|
795
|
+
|
|
796
|
+
# 解析主构造函数参数
|
|
797
|
+
primary_params = []
|
|
798
|
+
constructor_match = re.search(r'\(([^)]*)\)', lines[line_num - 1])
|
|
799
|
+
if constructor_match:
|
|
800
|
+
primary_params = self._parse_params(constructor_match.group(1))
|
|
801
|
+
|
|
802
|
+
# 解析继承
|
|
803
|
+
extends, implements = self._parse_inheritance(lines[line_num - 1])
|
|
804
|
+
|
|
805
|
+
# 提取类体
|
|
806
|
+
class_body, end_line = self._extract_block(lines, line_num)
|
|
807
|
+
|
|
808
|
+
# 创建类信息
|
|
809
|
+
class_info = KotlinClassInfo(
|
|
810
|
+
name=name,
|
|
811
|
+
line_start=line_num,
|
|
812
|
+
line_end=end_line,
|
|
813
|
+
class_type=class_type,
|
|
814
|
+
type_params=type_params,
|
|
815
|
+
primary_constructor_params=primary_params,
|
|
816
|
+
extends=extends,
|
|
817
|
+
implements=implements,
|
|
818
|
+
modifiers=modifiers,
|
|
819
|
+
annotations=[],
|
|
820
|
+
functions=[],
|
|
821
|
+
properties=[]
|
|
822
|
+
)
|
|
823
|
+
|
|
824
|
+
# 解析类体中的成员
|
|
825
|
+
if class_body:
|
|
826
|
+
self._parse_class_members(class_body, class_info)
|
|
827
|
+
|
|
828
|
+
return class_info
|
|
829
|
+
|
|
830
|
+
def _parse_class_members(self, class_body: str, class_info):
|
|
831
|
+
"""解析类成员"""
|
|
832
|
+
for line in class_body.split('\n'):
|
|
833
|
+
stripped = line.strip()
|
|
834
|
+
|
|
835
|
+
# 跳过空行和注释
|
|
836
|
+
if not stripped or stripped.startswith('//'):
|
|
837
|
+
continue
|
|
838
|
+
|
|
839
|
+
# 解析函数
|
|
840
|
+
if 'fun ' in stripped:
|
|
841
|
+
func = self._parse_function_line(stripped, 0)
|
|
842
|
+
if func:
|
|
843
|
+
class_info.functions.append(func)
|
|
844
|
+
|
|
845
|
+
# 解析属性
|
|
846
|
+
elif stripped.startswith('val ') or stripped.startswith('var '):
|
|
847
|
+
prop = self._parse_property_line(stripped, 0)
|
|
848
|
+
if prop:
|
|
849
|
+
class_info.properties.append(prop)
|
|
850
|
+
|
|
851
|
+
# ==================== 辅助方法 ====================
|
|
852
|
+
|
|
853
|
+
def _extract_block(self, lines: list, start_line: int) -> tuple[str, int]:
|
|
854
|
+
"""提取代码块(大括号包围)"""
|
|
855
|
+
brace_depth = 0
|
|
856
|
+
body_lines = []
|
|
857
|
+
started = False
|
|
858
|
+
|
|
859
|
+
for i in range(start_line - 1, len(lines)):
|
|
860
|
+
line = lines[i]
|
|
861
|
+
|
|
862
|
+
if '{' in line:
|
|
863
|
+
started = True
|
|
864
|
+
|
|
865
|
+
if started:
|
|
866
|
+
body_lines.append(line)
|
|
867
|
+
brace_depth += line.count('{') - line.count('}')
|
|
868
|
+
|
|
869
|
+
if brace_depth == 0:
|
|
870
|
+
return '\n'.join(body_lines), i + 1
|
|
871
|
+
|
|
872
|
+
return '\n'.join(body_lines), len(lines)
|
|
873
|
+
|
|
874
|
+
def _parse_params(self, params_str: str) -> list[dict]:
|
|
875
|
+
"""解析参数列表"""
|
|
876
|
+
if not params_str.strip():
|
|
877
|
+
return []
|
|
878
|
+
|
|
879
|
+
params = []
|
|
880
|
+
# 简单分割(不考虑嵌套的括号)
|
|
881
|
+
for param in params_str.split(','):
|
|
882
|
+
param = param.strip()
|
|
883
|
+
if not param:
|
|
884
|
+
continue
|
|
885
|
+
|
|
886
|
+
# 参数格式: name: Type = default
|
|
887
|
+
parts = param.split('=')
|
|
888
|
+
name_type = parts[0].strip()
|
|
889
|
+
default_value = parts[1].strip() if len(parts) > 1 else None
|
|
890
|
+
|
|
891
|
+
# 分离名称和类型
|
|
892
|
+
if ':' in name_type:
|
|
893
|
+
name_part, type_part = name_type.split(':', 1)
|
|
894
|
+
params.append({
|
|
895
|
+
'name': name_part.strip(),
|
|
896
|
+
'type': type_part.strip(),
|
|
897
|
+
'default_value': default_value
|
|
898
|
+
})
|
|
899
|
+
else:
|
|
900
|
+
params.append({
|
|
901
|
+
'name': name_type,
|
|
902
|
+
'type': 'Any',
|
|
903
|
+
'default_value': default_value
|
|
904
|
+
})
|
|
905
|
+
|
|
906
|
+
return params
|