diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 6284665..29e281e 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -1,26 +1,86 @@ import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.material.Button +import androidx.compose.material.ButtonColors import androidx.compose.material.MaterialTheme import androidx.compose.material.Text +import androidx.compose.material.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.ExperimentalTextApi +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Window import androidx.compose.ui.window.application +import core.SourceFile +@OptIn(ExperimentalTextApi::class) @Composable @Preview fun App() { - var text by remember { mutableStateOf("Hello, World!") } + var text by remember { mutableStateOf("") } + var isReady by remember { mutableStateOf(false) } + var source: SourceFile? = null MaterialTheme { - Button(onClick = { - text = "Hello, Desktop!" - }) { - Text(text) + Column( + modifier = Modifier.fillMaxSize() + .padding(horizontal = 10.dp, vertical = 5.dp), + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.Center + ) { + Text("请输入您的代码") + + TextField( + value = text, + onValueChange = { text = it }, + modifier = Modifier.fillMaxWidth(0.95f) + .weight(1f), + textStyle = TextStyle.Default.copy(fontFamily = FontFamily("monospace")) + ) + + Row( + modifier = Modifier.fillMaxWidth(0.9f) + .padding(vertical = 5.dp), + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically + ) { + Button( + modifier = Modifier.padding(vertical = 10.dp), + onClick = { + val s = SourceFile(text.toString()) + text = s.content + source = s + }, + ) { + Text("格式化") + } + + Button( + modifier = Modifier.padding(vertical = 10.dp), + onClick = { + source = SourceFile(text.toString()) + isReady = true + }, + enabled = !isReady + ) { + Text("分析") + } + } } + } } diff --git a/src/main/kotlin/core/GraphvizHelper.kt b/src/main/kotlin/core/GraphvizHelper.kt new file mode 100644 index 0000000..25979a1 --- /dev/null +++ b/src/main/kotlin/core/GraphvizHelper.kt @@ -0,0 +1,91 @@ +package core + +data class Relation(val from: String, val to: String) + +fun generateGraph(relations: Map>, crossLabelPaths: List? = null): String { + val sb = StringBuilder() + sb.append("digraph G {\n") + + for ((label, edges) in relations) { + sb.append(" subgraph cluster_$label {\n") + sb.append(" label = \"$label\";\n") + + val uniquePaths = mutableSetOf() + + for (relation in edges) { + uniquePaths.add("${relation.from} -> ${relation.to}") + sb.append(" ${relation.from} -> ${relation.to};\n") + } + + sb.append(" }\n") + } + + if (crossLabelPaths != null) { + // 处理跨函数 + for (path in crossLabelPaths) { + val parts = path.split(":") + if (parts.size == 2) { + val subPath = parts[1].split("->") // 栗子: main:z->a->m + if (subPath.size >= 2) { + val fromNode = subPath[0] // 这里就是z + val toNode = subPath[1] // 下一个就是 + // from 到 to 用虚线 + sb.append(" $fromNode -> $toNode [style=dashed];\n") + } + } + } + } + + sb.append("}\n") + return sb.toString() +} + + +fun parseRelation(funcName: String, parse: String): Map>{ + val list = mutableListOf() + val added = mutableListOf() + parse.split('\n').forEach { line -> + val split = line.split("->") + if (split.size < 2){ + return@forEach + } + for (i in 0...getInvokeGraph(funcName: String): String { + val relations = mutableMapOf>() + val func = this.find(funcName) ?: return "" + + this.forEach { + relations.putAll(parseRelation(it.name,it.getTraceTree().getStringRepr())) + } + + val crossLabelPaths = this.getInvokeTree(func.name).getFuncRepr(func.name) + return generateGraph(relations,crossLabelPaths.split('\n')) +} + +fun main() { + val relations = mapOf( + "main" to listOf( + Relation("x", "y"), + Relation("z", "x"), + Relation("c", "d") + ), + "A" to listOf( + Relation("a", "m") + ) + ) + + val graphCode = generateGraph(relations) + println(graphCode) +} diff --git a/src/main/kotlin/core/SourceFile.kt b/src/main/kotlin/core/SourceFile.kt index 476b172..8b88c20 100644 --- a/src/main/kotlin/core/SourceFile.kt +++ b/src/main/kotlin/core/SourceFile.kt @@ -4,6 +4,7 @@ package core import java.io.File import core.CLanguage.Companion.OPERATION.* + class SourceFile { companion object { val function_define_regex = """\b([a-zA-Z_][a-zA-Z0-9_]*)\s*\([^)]*\)\s*\{""".toRegex() // main(){ 匹配函数名 @@ -18,7 +19,7 @@ class SourceFile { } } - private val content: String + val content: String private var functions: List = emptyList() constructor(file: File) { @@ -105,15 +106,18 @@ class SourceFile { fun main() { val sourceFile = SourceFile(File("/home/kagura/repo/VariableRelation/src/main/CTests/test.c")) val funcs = sourceFile.parseFunction() + val relations = mutableMapOf>() 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}") } println(it.getTraceTree().getStringRepr()) + relations.putAll(parseRelation(it.name,it.getTraceTree().getStringRepr())) } println(funcs.getInvokeTree("main").getFuncRepr("main")) - println(sourceFile.getDef()) println(sourceFile.getUse()) + println(generateGraph(relations)) + println(funcs.getInvokeGraph("main")) } \ No newline at end of file