graphviz and ui
This commit is contained in:
parent
1ce8f94be6
commit
c7f8a7c30b
3 changed files with 162 additions and 7 deletions
|
@ -1,26 +1,86 @@
|
||||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
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.Button
|
||||||
|
import androidx.compose.material.ButtonColors
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.material.TextField
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
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.Window
|
||||||
import androidx.compose.ui.window.application
|
import androidx.compose.ui.window.application
|
||||||
|
import core.SourceFile
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTextApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@Preview
|
@Preview
|
||||||
fun App() {
|
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 {
|
MaterialTheme {
|
||||||
Button(onClick = {
|
Column(
|
||||||
text = "Hello, Desktop!"
|
modifier = Modifier.fillMaxSize()
|
||||||
}) {
|
.padding(horizontal = 10.dp, vertical = 5.dp),
|
||||||
Text(text)
|
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("分析")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
91
src/main/kotlin/core/GraphvizHelper.kt
Normal file
91
src/main/kotlin/core/GraphvizHelper.kt
Normal 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)
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ package core
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import core.CLanguage.Companion.OPERATION.*
|
import core.CLanguage.Companion.OPERATION.*
|
||||||
|
|
||||||
class SourceFile {
|
class SourceFile {
|
||||||
companion object {
|
companion object {
|
||||||
val function_define_regex = """\b([a-zA-Z_][a-zA-Z0-9_]*)\s*\([^)]*\)\s*\{""".toRegex() // main(){ 匹配函数名
|
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()
|
private var functions: List<CFunction> = emptyList()
|
||||||
|
|
||||||
constructor(file: File) {
|
constructor(file: File) {
|
||||||
|
@ -105,15 +106,18 @@ class SourceFile {
|
||||||
fun main() {
|
fun main() {
|
||||||
val sourceFile = SourceFile(File("/home/kagura/repo/VariableRelation/src/main/CTests/test.c"))
|
val sourceFile = SourceFile(File("/home/kagura/repo/VariableRelation/src/main/CTests/test.c"))
|
||||||
val funcs = sourceFile.parseFunction()
|
val funcs = sourceFile.parseFunction()
|
||||||
|
val relations = mutableMapOf<String, List<Relation>>()
|
||||||
funcs.forEach {
|
funcs.forEach {
|
||||||
println("${it.name}(${it.params}) -> ${it.returnType} {\n${it.content}\n} ")
|
println("${it.name}(${it.params}) -> ${it.returnType} {\n${it.content}\n} ")
|
||||||
it.cParser.sentenceList.forEach{ s->
|
it.cParser.sentenceList.forEach{ s->
|
||||||
println("${s.left},${s.right},${s.type}")
|
println("${s.left},${s.right},${s.type}")
|
||||||
}
|
}
|
||||||
println(it.getTraceTree().getStringRepr())
|
println(it.getTraceTree().getStringRepr())
|
||||||
|
relations.putAll(parseRelation(it.name,it.getTraceTree().getStringRepr()))
|
||||||
}
|
}
|
||||||
println(funcs.getInvokeTree("main").getFuncRepr("main"))
|
println(funcs.getInvokeTree("main").getFuncRepr("main"))
|
||||||
|
|
||||||
println(sourceFile.getDef())
|
println(sourceFile.getDef())
|
||||||
println(sourceFile.getUse())
|
println(sourceFile.getUse())
|
||||||
|
println(generateGraph(relations))
|
||||||
|
println(funcs.getInvokeGraph("main"))
|
||||||
}
|
}
|
Loading…
Reference in a new issue