Add
This commit is contained in:
parent
2d2597e5df
commit
1ba58caa2b
8 changed files with 273 additions and 47 deletions
|
@ -5,6 +5,7 @@
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="gradleJvm" value="openjdk-21" />
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
<file type="web" url="file://$PROJECT_DIR$" />
|
<file type="web" url="file://$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_22" default="true" project-jdk-name="openjdk-22" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="openjdk-22" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -12,8 +12,8 @@ int A(int a){
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
char Test(int para1,char para2){
|
//char Test(int para1,char para2){
|
||||||
int c1 = para1, i_doNothingbuttest = 2;
|
// int c1 = para1, i_doNothingbuttest = 2;
|
||||||
c1 = para2;
|
// c1 = para2;
|
||||||
return c2;
|
// return c2;
|
||||||
}
|
//}
|
|
@ -1,19 +1,23 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import core.CLanguage.Companion.typenameToTypeEnum
|
import core.CLanguage.Companion.typenameToTypeEnum
|
||||||
|
import org.w3c.dom.Node
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class CFunction(
|
class CFunction(
|
||||||
val name: String, source: String // 函数名
|
val name: String, source: String // 函数名
|
||||||
) {
|
) {
|
||||||
val returnType: CLanguage.Companion.CTYPES // 返回类型
|
val returnType: CLanguage.Companion.CTYPES // 返回类型
|
||||||
val params: List<Map<String,CLanguage.Companion.CTYPES>>? // 参数 Map<name,T> 与实际相反
|
val params: List<Map<String, CLanguage.Companion.CTYPES>>? // 参数 Map<name,T> 与实际相反
|
||||||
|
var paramNameList: List<String> = emptyList()
|
||||||
|
private set
|
||||||
val content: String
|
val content: String
|
||||||
|
val cParser: CParser
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.params = parseParameters(source)
|
this.params = parseParameters(source)
|
||||||
this.content = parseContent(source)
|
this.content = parseContent(source)
|
||||||
|
cParser = CParser(content, paramNameList)
|
||||||
returnType = typenameToTypeEnum(parseReturnType(source)) ?: CLanguage.Companion.CTYPES.void
|
returnType = typenameToTypeEnum(parseReturnType(source)) ?: CLanguage.Companion.CTYPES.void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +31,11 @@ class CFunction(
|
||||||
val paramPattern = Regex("$name[ |\n]?\\((.*?)\\)[ |\n]?\\{") // main[这里随便空格换行](params...){
|
val paramPattern = Regex("$name[ |\n]?\\((.*?)\\)[ |\n]?\\{") // main[这里随便空格换行](params...){
|
||||||
val match = paramPattern.find(source)
|
val match = paramPattern.find(source)
|
||||||
val paramsList = match?.groups?.get(1)?.value?.split(",")?.map { it.trim() } // 函数最开始的那个括号里面
|
val paramsList = match?.groups?.get(1)?.value?.split(",")?.map { it.trim() } // 函数最开始的那个括号里面
|
||||||
|
paramNameList = if (paramsList.isNullOrEmpty() || paramsList.first().isEmpty()) {
|
||||||
|
emptyList()
|
||||||
|
} else {
|
||||||
|
paramsList.map { it.split(' ').last() }
|
||||||
|
}
|
||||||
return paramsList?.map { param ->
|
return paramsList?.map { param ->
|
||||||
val parts = param.split(" ")
|
val parts = param.split(" ")
|
||||||
val type = typenameToTypeEnum(parts[0]) ?: return@map emptyMap()
|
val type = typenameToTypeEnum(parts[0]) ?: return@map emptyMap()
|
||||||
|
@ -44,24 +52,51 @@ class CFunction(
|
||||||
// 找到 {} 的位置
|
// 找到 {} 的位置
|
||||||
var firstOccur = -1
|
var firstOccur = -1
|
||||||
var bracket = 0 // 下一次到0就说明匹配完了
|
var bracket = 0 // 下一次到0就说明匹配完了
|
||||||
for (i in index..source.lastIndex){
|
for (i in index..source.lastIndex) {
|
||||||
if (source[i] == '{'){
|
if (source[i] == '{') {
|
||||||
if (firstOccur == -1){ // 第一次遇见
|
if (firstOccur == -1) { // 第一次遇见
|
||||||
firstOccur = i
|
firstOccur = i
|
||||||
}
|
}
|
||||||
bracket ++
|
bracket++
|
||||||
}else if (source[i] == '}'){
|
} else if (source[i] == '}') {
|
||||||
bracket --
|
bracket--
|
||||||
if (bracket == 0){
|
if (bracket == 0) {
|
||||||
return source.substring(firstOccur+1..<i).trim() // 不加大括号
|
return source.substring(firstOccur + 1..<i).trim() // 不加大括号
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun main(){
|
fun getTraceTree(): List<TraceTree> {
|
||||||
val fun_main = CFunction("Test", File("/home/kagura/repo/VariableRelation/src/main/CTests/test.c").readText())
|
// 初始化 Trace Tree
|
||||||
println("${fun_main.name}(${fun_main.params}) -> ${fun_main.returnType} {${fun_main.content}} ")
|
val nodes = mutableListOf<TraceTree>()
|
||||||
|
cParser.defineList.forEach {
|
||||||
|
nodes.add(TraceTree(it, null, null))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 然后根据句子来获得 TraceTree
|
||||||
|
cParser.sentenceList.forEachIndexed { index, sentence ->
|
||||||
|
if (sentence.type == CLanguage.Companion.OPERATION.DEFINE_VALUE_PARAM || sentence.type == CLanguage.Companion.OPERATION.CHANGE_VALUE_PARAM) {
|
||||||
|
// 例子:
|
||||||
|
// x,A(z),CHANGE_VALUE
|
||||||
|
// A,z,CHANGE_VALUE_PARAM
|
||||||
|
// y,x,CHANGE_VALUE
|
||||||
|
// -,x,CHANGE_VALUE_PARAM
|
||||||
|
if (index > 1) { // 按理来说肯定成立,加个检查防止意外
|
||||||
|
val left = cParser.sentenceList[index - 1].left
|
||||||
|
val lNode = nodes.findNode(left)
|
||||||
|
val rNode = nodes.findNode(sentence.right)
|
||||||
|
val funName = sentence.left
|
||||||
|
if (lNode != null && rNode != null) {
|
||||||
|
lNode.changedBy = rNode
|
||||||
|
if (funName != "-") {
|
||||||
|
lNode.upperFuncName = funName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodes.getDeepest()
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class CLanguage {
|
class CLanguage {
|
||||||
companion object {
|
companion object {
|
||||||
val types = listOf("void","char","int","short","long","float","double","struct")
|
val types = listOf("void","char","int","short","long","float","double","struct")
|
||||||
enum class OPERATION {
|
enum class OPERATION {
|
||||||
DEFINE_VALUE, // `type` identifier = RHand 题目里 def-use
|
DEFINE_VALUE, // `type` identifier = RHand 题目里 def-use
|
||||||
FUNCTION_INVOCATION, // func(value-param) 题目里
|
FUNCTION_INVOCATION, // func(value-param) 题目里
|
||||||
VALUE_CHANGE, // identifier = RHAND
|
CHANGE_VALUE, // identifier = RHAND
|
||||||
|
DEFINE_VALUE_PARAM, // int a=b, 一直接在上一个后面
|
||||||
|
CHANGE_VALUE_PARAM, // a = b
|
||||||
RETURN // 字面意思
|
RETURN // 字面意思
|
||||||
}
|
}
|
||||||
enum class CTYPES {
|
enum class CTYPES {
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import java.io.File
|
class Sentence(val left: String, val right: String, val type: CLanguage.Companion.OPERATION)
|
||||||
|
|
||||||
class CParser(content: String) {
|
class CParser(content: String, paramNameList: List<String>) {
|
||||||
class Sentence(val left: String, val right: String, val type: CLanguage.Companion.OPERATION)
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val lrexp_regex = "(.*)[ ]?=[ ]?(.*)[ ]?;?$".toRegex() // 左1右2, 空格去掉前面type
|
val lrexp_regex = "(.*)[ ]?=[ ]?(.*)[ ]?;?$".toRegex() // 左1右2
|
||||||
val func_invoke_regex = "(.*)[ ]?\\((.*)\\)[ ]?$".toRegex() // 匹配到的是1函数名2参数列表
|
val func_invoke_regex = "(.*)[ ]?\\((.*)\\)[ ]?$".toRegex() // 匹配到的是1:函数名2:参数列表
|
||||||
}
|
}
|
||||||
|
|
||||||
val sentenceList = mutableListOf<Sentence>()
|
val sentenceList = mutableListOf<Sentence>()
|
||||||
val defineList = mutableListOf<String>()
|
val defineList = mutableListOf<String>()
|
||||||
|
|
||||||
init { // 第一轮解析获得基本语义
|
init {
|
||||||
|
// 把参数加进去
|
||||||
|
defineList.addAll(paramNameList)
|
||||||
|
// 第一轮解析获得基本语义
|
||||||
content.split(';').forEach {
|
content.split(';').forEach {
|
||||||
val line = it.trim()
|
val line = it.trim()
|
||||||
if (line.contains('=')) { // 出现了left hand 和 right hand
|
if (line.contains('=')) { // 出现了left hand 和 right hand
|
||||||
|
@ -26,6 +28,7 @@ class CParser(content: String) {
|
||||||
val rightHand = match.groupValues[2].trim() // Right-hand side (value)
|
val rightHand = match.groupValues[2].trim() // Right-hand side (value)
|
||||||
sentenceList.add(Sentence(leftHand, rightHand, CLanguage.Companion.OPERATION.DEFINE_VALUE))
|
sentenceList.add(Sentence(leftHand, rightHand, CLanguage.Companion.OPERATION.DEFINE_VALUE))
|
||||||
defineList.add(leftHand)
|
defineList.add(leftHand)
|
||||||
|
sentenceList.addAll(rightHandSimpleParser(rightHand, CLanguage.Companion.OPERATION.DEFINE_VALUE))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // 重新定义
|
} else { // 重新定义
|
||||||
|
@ -33,33 +36,67 @@ class CParser(content: String) {
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
val leftHand = match.groupValues[1].trim() // Left-hand side (variable)
|
val leftHand = match.groupValues[1].trim() // Left-hand side (variable)
|
||||||
val rightHand = match.groupValues[2].trim() // Right-hand side (value)
|
val rightHand = match.groupValues[2].trim() // Right-hand side (value)
|
||||||
sentenceList.add(Sentence(leftHand, rightHand, CLanguage.Companion.OPERATION.VALUE_CHANGE))
|
sentenceList.add(Sentence(leftHand, rightHand, CLanguage.Companion.OPERATION.CHANGE_VALUE))
|
||||||
|
sentenceList.addAll(rightHandSimpleParser(rightHand, CLanguage.Companion.OPERATION.CHANGE_VALUE))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (line.contains("return")) { // return
|
} else if (line.contains("return")) { // return
|
||||||
val returnValue = line.split(' ').last()
|
val returnValue = line.split(' ').last()
|
||||||
sentenceList.add(Sentence("return",returnValue,CLanguage.Companion.OPERATION.RETURN))
|
sentenceList.add(Sentence("return", returnValue, CLanguage.Companion.OPERATION.RETURN))
|
||||||
} else { // 调用函数,解析参数就行了
|
} else { // 调用函数,解析参数就行了
|
||||||
val match = func_invoke_regex.find(line) // 如果是后面的就要这个
|
val match = func_invoke_regex.find(line) // 如果是后面的就要这个
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
val func_name = match.groupValues[1].trim().split(' ').last() // Left-hand side (variable)
|
val func_name = match.groupValues[1].trim().split(' ').last() // Left-hand side (variable)
|
||||||
val args = match.groupValues[2].trim() // Right-hand side (value)
|
val args = match.groupValues[2].trim() // Right-hand side (value)
|
||||||
args.split(',').forEach { arg ->
|
args.split(',').forEach { arg ->
|
||||||
if (arg in defineList){ // 定义过的变量
|
if (arg in defineList) { // 定义过的变量
|
||||||
sentenceList.add(Sentence(func_name,arg,CLanguage.Companion.OPERATION.FUNCTION_INVOCATION))
|
sentenceList.add(Sentence(func_name, arg, CLanguage.Companion.OPERATION.FUNCTION_INVOCATION))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun main() {
|
private fun rightHandSimpleParser(right: String, type: CLanguage.Companion.OPERATION): List<Sentence> { // 解析右边表达式的
|
||||||
val fun_main = CFunction("main", File("/home/kagura/repo/VariableRelation/src/main/CTests/test.c").readText())
|
fun getType(): CLanguage.Companion.OPERATION {
|
||||||
val source = fun_main.content
|
return when (type) { // 改变类型
|
||||||
val cParser = CParser(source)
|
CLanguage.Companion.OPERATION.DEFINE_VALUE -> {
|
||||||
cParser.sentenceList.forEach {
|
CLanguage.Companion.OPERATION.DEFINE_VALUE_PARAM
|
||||||
println("${it.left}<-${it.right}, ${it.type}")
|
}
|
||||||
|
|
||||||
|
CLanguage.Companion.OPERATION.CHANGE_VALUE -> {
|
||||||
|
CLanguage.Companion.OPERATION.CHANGE_VALUE_PARAM
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
CLanguage.Companion.OPERATION.RETURN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = mutableListOf<Sentence>()
|
||||||
|
// 先看看是不是调用函数
|
||||||
|
if ('(' in right) {
|
||||||
|
val match = func_invoke_regex.find(right)
|
||||||
|
if (match != null) {
|
||||||
|
val funName = match.groupValues[1].trim()
|
||||||
|
val params = match.groupValues[2].trim().split(',')
|
||||||
|
for (param in params) {
|
||||||
|
if (param in defineList) { // 定义过的才操作
|
||||||
|
result.add(Sentence(funName, param, getType()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (right in defineList) { // 看看有没有使用值
|
||||||
|
result.add(
|
||||||
|
Sentence(
|
||||||
|
"-", right, getType()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,16 +3,112 @@ package core
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class SourceFile {
|
class SourceFile {
|
||||||
private val content: String
|
companion object {
|
||||||
|
val function_define_regex = """\b([a-zA-Z_][a-zA-Z0-9_]*)\s*\([^)]*\)\s*\{""".toRegex() // main(){ 匹配函数名
|
||||||
|
|
||||||
constructor(file: File){
|
fun deleteNotes(src: String): String {
|
||||||
if (!file.isFile){
|
return src.lines()
|
||||||
|
.filter { line -> !line.trimStart().startsWith("#include") && !line.trimStart().startsWith("//") }
|
||||||
|
.joinToString("\n") { line ->
|
||||||
|
val withoutTrailingComment = line.replace(Regex("//.*$"), "")
|
||||||
|
withoutTrailingComment.trimStart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val content: String
|
||||||
|
private var functions: List<CFunction> = emptyList()
|
||||||
|
|
||||||
|
constructor(file: File) {
|
||||||
|
if (!file.isFile) {
|
||||||
throw NoSuchFileException(file)
|
throw NoSuchFileException(file)
|
||||||
}
|
}
|
||||||
content = file.readText()
|
content = deleteNotes(file.readText())
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(content: String){
|
constructor(content: String) {
|
||||||
this.content = content
|
this.content = deleteNotes(content.trim())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun parseFunction(): List<CFunction> { // 找所有的函数定义
|
||||||
|
val result = mutableListOf<CFunction>()
|
||||||
|
function_define_regex.findAll(content).forEach {
|
||||||
|
if (it.groups.size >= 2 && it.groups[1] != null) {
|
||||||
|
result.add(CFunction(it.groups[1]!!.value, content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
functions = result
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDef(): Map<String, List<String>> { // 获取所有的定义
|
||||||
|
val result = mutableMapOf<String, List<String>>()
|
||||||
|
functions.forEach {
|
||||||
|
result[it.name] = it.cParser.defineList
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUse(): List<List<String>> {
|
||||||
|
val globalResult = mutableListOf<List<String>>()
|
||||||
|
functions.forEach {
|
||||||
|
val funcResult = mutableListOf<String>()
|
||||||
|
|
||||||
|
// 第一个处理参数
|
||||||
|
if (it.paramNameList.isEmpty()) {
|
||||||
|
funcResult.add("void")
|
||||||
|
} else {
|
||||||
|
it.paramNameList.forEach { param ->
|
||||||
|
funcResult.add("$param-def")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后面的
|
||||||
|
val useCache = StringBuilder()
|
||||||
|
|
||||||
|
it.cParser.sentenceList.forEach { sentence ->
|
||||||
|
when (sentence.type) {
|
||||||
|
CLanguage.Companion.OPERATION.FUNCTION_INVOCATION, CLanguage.Companion.OPERATION.RETURN -> {
|
||||||
|
if (useCache.isNotEmpty()) {
|
||||||
|
funcResult.add(useCache.toString())
|
||||||
|
useCache.clear() // 清空
|
||||||
|
}
|
||||||
|
funcResult.add("${sentence.right}-use")
|
||||||
|
}
|
||||||
|
|
||||||
|
CLanguage.Companion.OPERATION.DEFINE_VALUE, CLanguage.Companion.OPERATION.CHANGE_VALUE -> {
|
||||||
|
if (useCache.isNotEmpty()) {
|
||||||
|
funcResult.add(useCache.toString())
|
||||||
|
useCache.clear()
|
||||||
|
}
|
||||||
|
useCache.append("${sentence.left}-def")
|
||||||
|
}
|
||||||
|
|
||||||
|
CLanguage.Companion.OPERATION.DEFINE_VALUE_PARAM, CLanguage.Companion.OPERATION.CHANGE_VALUE_PARAM -> {
|
||||||
|
useCache.append(";${sentence.right}-use")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (useCache.isNotEmpty()) {
|
||||||
|
funcResult.add(useCache.toString())
|
||||||
|
}
|
||||||
|
globalResult.add(funcResult)
|
||||||
|
}
|
||||||
|
return globalResult
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
val sourceFile = SourceFile(File("/home/kagura/repo/VariableRelation/src/main/CTests/test.c"))
|
||||||
|
val funcs = sourceFile.parseFunction()
|
||||||
|
funcs.forEach {
|
||||||
|
println("${it.name}(${it.params}) -> ${it.returnType} {\n${it.content}\n} ")
|
||||||
|
// it.cParser.sentenceList.forEach{ s->
|
||||||
|
// println("${s.left},${s.right},${s.type}")
|
||||||
|
// }
|
||||||
|
it.getTraceTree().print()
|
||||||
|
}
|
||||||
|
|
||||||
|
println(sourceFile.getDef())
|
||||||
|
println(sourceFile.getUse())
|
||||||
}
|
}
|
57
src/main/kotlin/core/TraceTree.kt
Normal file
57
src/main/kotlin/core/TraceTree.kt
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
data class TraceTree(
|
||||||
|
val name: String,
|
||||||
|
var changedBy: TraceTree?,
|
||||||
|
var upperFuncName: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
fun List<TraceTree>.findNode(name: String): TraceTree? {
|
||||||
|
for (tree in this) {
|
||||||
|
if (tree.name == name) {
|
||||||
|
return tree
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<TraceTree>.print() {
|
||||||
|
fun traceChain(node: TraceTree?): String {
|
||||||
|
return if (node == null) {
|
||||||
|
"" // 读取完了
|
||||||
|
} else {
|
||||||
|
traceChain(node.changedBy) + if (node.changedBy != null) {
|
||||||
|
"->"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
} + node.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.forEach { traceTree ->
|
||||||
|
println(traceChain(traceTree))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun List<TraceTree>.getDeepest(): List<TraceTree> {
|
||||||
|
// 使用 Map 记录每个节点的深度
|
||||||
|
val depthMap = mutableMapOf<TraceTree, Int>()
|
||||||
|
|
||||||
|
// 计算每个节点的深度(从终点向上追溯)
|
||||||
|
for (trace in this) {
|
||||||
|
var depth = 0
|
||||||
|
var current = trace
|
||||||
|
while (current.changedBy != null) {
|
||||||
|
depth++
|
||||||
|
current = current.changedBy!!
|
||||||
|
}
|
||||||
|
// 将链起点的深度记录在 depthMap 中
|
||||||
|
depthMap[trace] = depth
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找到最大深度值
|
||||||
|
val maxDepth = depthMap.values.maxOrNull() ?: 0
|
||||||
|
|
||||||
|
// 返回具有最大深度的节点
|
||||||
|
return depthMap.filterValues { it == maxDepth }.keys.toList()
|
||||||
|
}
|
Loading…
Reference in a new issue