From ce431e48f76d2933f81844bae376a128b8270f59 Mon Sep 17 00:00:00 2001 From: Phodal Huang Date: Thu, 1 Feb 2024 22:19:50 +0800 Subject: [PATCH] feat(chapi-ast-c): add override to addSource method #24 This commit adds the `override` keyword to the `addSource` method in the `CAnalyser` class. This ensures that the method is properly overridden from the parent class. --- chapi-ast-c/.gitignore | 1 + .../main/kotlin/chapi/ast/cast/CAnalyser.kt | 5 +-- .../chapi/ast/cast/CFullIdentListener.kt | 13 ++++-- .../chapi/ast/cast/CFullIdentListenerTest.kt | 41 +++++++++++++++++-- .../src/main/kotlin/chapi/parser/Analyser.kt | 10 +++++ 5 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 chapi-ast-c/.gitignore diff --git a/chapi-ast-c/.gitignore b/chapi-ast-c/.gitignore new file mode 100644 index 00000000..890e9233 --- /dev/null +++ b/chapi-ast-c/.gitignore @@ -0,0 +1 @@ +result.json diff --git a/chapi-ast-c/src/main/kotlin/chapi/ast/cast/CAnalyser.kt b/chapi-ast-c/src/main/kotlin/chapi/ast/cast/CAnalyser.kt index 5e211c98..1191d293 100644 --- a/chapi-ast-c/src/main/kotlin/chapi/ast/cast/CAnalyser.kt +++ b/chapi-ast-c/src/main/kotlin/chapi/ast/cast/CAnalyser.kt @@ -23,10 +23,7 @@ open class CAnalyser : Analyser { pp.listener = DefaultPreprocessorListener() } - /** - * Adds a source code to the program, for example, C header files. - */ - fun addSource(code: String) { + override fun addSource(code: String) { pp.addInput(LexerSource(InputStreamReader(code.byteInputStream()), true)) } diff --git a/chapi-ast-c/src/main/kotlin/chapi/ast/cast/CFullIdentListener.kt b/chapi-ast-c/src/main/kotlin/chapi/ast/cast/CFullIdentListener.kt index 9b3be8bb..e41702da 100644 --- a/chapi-ast-c/src/main/kotlin/chapi/ast/cast/CFullIdentListener.kt +++ b/chapi-ast-c/src/main/kotlin/chapi/ast/cast/CFullIdentListener.kt @@ -63,11 +63,18 @@ open class CFullIdentListener(fileName: String, includes: MutableList) : ctx?.structDeclarationList()?.structDeclaration()?.forEach { structDeclCtx -> /// for forward struct declaration structDeclCtx.structDeclaratorList()?.let { - val type = structDeclCtx.specifierQualifierList()?.typeSpecifier()?.let { - val specifier = it.structOrUnionSpecifier() + var type = structDeclCtx.specifierQualifierList()?.typeSpecifier()?.let { typeSpec -> + val specifier = typeSpec.structOrUnionSpecifier() specifier?.structOrUnion()?.text + " " + specifier?.Identifier()?.text } - val value = structDeclCtx.specifierQualifierList()?.specifierQualifierList()?.text ?: "" + var value: String? = null + + if (type == null || type == "null null") { + type = structDeclCtx.specifierQualifierList()?.typeSpecifier()?.text ?: "" + value = it.structDeclarator()?.firstOrNull()?.declarator()?.text ?: "" + } else { + value = structDeclCtx.specifierQualifierList()?.specifierQualifierList()?.text ?: "" + } val field = CodeField( TypeType = type ?: "", diff --git a/chapi-ast-c/src/test/kotlin/chapi/ast/cast/CFullIdentListenerTest.kt b/chapi-ast-c/src/test/kotlin/chapi/ast/cast/CFullIdentListenerTest.kt index d9f70d76..2e19f463 100644 --- a/chapi-ast-c/src/test/kotlin/chapi/ast/cast/CFullIdentListenerTest.kt +++ b/chapi-ast-c/src/test/kotlin/chapi/ast/cast/CFullIdentListenerTest.kt @@ -1,9 +1,12 @@ package chapi.ast.cast +import chapi.domain.core.CodeContainer import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.runBlocking +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import org.junit.jupiter.api.Test import java.io.File import kotlin.test.assertEquals @@ -14,21 +17,33 @@ internal class CFullIdentListenerTest { @Test fun allGrammarUnderResources() { val content = this::class.java.getResource("/grammar")!!.toURI() -// val content = "/Users/phodal/Downloads/redis-unstable" +// val content = "/Users/phodal/Downloads/redis-unstable/deps/lua" val totalStart = System.currentTimeMillis() runBlocking { - File(content).walkTopDown().asFlow().mapNotNull { + val analyser = CAnalyser() + val fileFlow = File(content).walkTopDown().asFlow() + fileFlow.mapNotNull { + if (it.isFile && (it.extension == "c" || it.extension == "h")) { + analyser.addSource(it.readText()) + } + } + + val result: MutableList = mutableListOf() + fileFlow.mapNotNull { if (it.isFile && (it.extension == "c" || it.extension == "h")) { val start = System.currentTimeMillis() println("Analyse ${it.path}") - val analysis = CAnalyser().analysis(it.readText(), it.name) + val analysis = analyser.analysis(it.readText(), it.name) val end = System.currentTimeMillis() println("cost ${end - start}ms") - analysis + result += analysis } else { null } }.collect() + + // log to file + File("result.json").writeText(Json.encodeToString(result)) } val totalEnd = System.currentTimeMillis() @@ -78,6 +93,24 @@ struct list_el { assertEquals(codeFile.DataStructures[0].Fields[0].TypeValue, "val") } + @Test + internal fun shouldIdentifyStructWithPointer() { + val code = """ +typedef struct Mbuffer { + char *buffer; + size_t n; + size_t buffsize; +} Mbuffer; + +""" + val codeFile = CAnalyser().analysis(code, "helloworld.c") + + assertEquals(codeFile.DataStructures.size, 1) + assertEquals(codeFile.DataStructures[0].Fields.size, 3) + assertEquals(codeFile.DataStructures[0].Fields[0].TypeType, "char") + assertEquals(codeFile.DataStructures[0].Fields[0].TypeValue, "*buffer") + } + @Test internal fun shouldIdentifyStructFunction() { val code = """ diff --git a/chapi-domain/src/main/kotlin/chapi/parser/Analyser.kt b/chapi-domain/src/main/kotlin/chapi/parser/Analyser.kt index e9c5de98..698d3572 100644 --- a/chapi-domain/src/main/kotlin/chapi/parser/Analyser.kt +++ b/chapi-domain/src/main/kotlin/chapi/parser/Analyser.kt @@ -1,6 +1,7 @@ package chapi.parser import chapi.domain.core.CodeContainer +import chapi.domain.core.Since enum class ParseMode { Basic, Full @@ -8,6 +9,15 @@ enum class ParseMode { // todo: add support for kotlin & java interface Analyser { + + /** + * Adds a source code to the program, for example, C header files. + */ + @Since("2.3.4") + fun addSource(code: String) { + + } + fun analysis(code: String, filePath: String): CodeContainer }