graphviz and ui

This commit is contained in:
kagura 2024-10-29 16:46:21 +08:00
parent 1ce8f94be6
commit c7f8a7c30b
3 changed files with 162 additions and 7 deletions

View file

@ -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("分析")
}
}
}
}
}

View file

@ -0,0 +1,91 @@
package core
data class Relation(val from: String, val to: String)
fun generateGraph(relations: Map<String, List<Relation>>, crossLabelPaths: List<String>? = 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<String>()
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<String, List<Relation>>{
val list = mutableListOf<Relation>()
val added = mutableListOf<Int>()
parse.split('\n').forEach { line ->
val split = line.split("->")
if (split.size < 2){
return@forEach
}
for (i in 0..<split.lastIndex){
val hash = "${split[i]}->${split[i+1]}".hashCode()
if (hash !in added){
list.add(Relation(split[i],split[i+1]))
added.add(hash)
}
}
}
return mapOf(
funcName to list
)
}
fun List<CFunction>.getInvokeGraph(funcName: String): String {
val relations = mutableMapOf<String, List<Relation>>()
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)
}

View file

@ -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<CFunction> = 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<String, List<Relation>>()
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"))
}