From 67e85786f173d3145c0a8888baa151bf6440ee13 Mon Sep 17 00:00:00 2001 From: Phodal Huang Date: Fri, 25 Oct 2024 10:55:59 +0800 Subject: [PATCH] feat(protobuf-parser): enhance parsing for enum definitions and inner structures #31 Previously, the Protobuf parser did not fully support parsing enum definitions and inner structures within message definitions. This commit introduces the necessary changes to: - Update `ProtobufFullIdentListener` to properly handle `EnumDefContext` and add support for inner structures. - Refactor `constructMessageDef` and introduce `constructEnum` to build `CodeDataStruct` for enums. - Add a test case in `ProtobufAnalyserTest` to validate the parsing of enum definitions. These enhancements enable more comprehensive analysis of Protobuf files, including the handling of nested and enum-related definitions. --- .../ast/protobuf/ProtobufFullIdentListener.kt | 58 ++++++++++++++----- .../ast/protobuf/ProtobufAnalyserTest.kt | 29 ++++++++++ .../chapi/domain/core/CodeDataStruct.kt | 3 + 3 files changed, 76 insertions(+), 14 deletions(-) diff --git a/chapi-ast-protobuf/src/main/kotlin/chapi/ast/protobuf/ProtobufFullIdentListener.kt b/chapi-ast-protobuf/src/main/kotlin/chapi/ast/protobuf/ProtobufFullIdentListener.kt index c218c9f6..f1d78f12 100644 --- a/chapi-ast-protobuf/src/main/kotlin/chapi/ast/protobuf/ProtobufFullIdentListener.kt +++ b/chapi-ast-protobuf/src/main/kotlin/chapi/ast/protobuf/ProtobufFullIdentListener.kt @@ -5,6 +5,7 @@ import chapi.ast.antlr.Protobuf3Parser import chapi.domain.core.CodeContainer import chapi.domain.core.CodeDataStruct import chapi.domain.core.CodeField +import chapi.domain.core.DataStructType class ProtobufFullIdentListener(var fileName: String) : Protobuf3BaseListener() { private var codeContainer: CodeContainer = CodeContainer(FullName = fileName) @@ -14,14 +15,19 @@ class ProtobufFullIdentListener(var fileName: String) : Protobuf3BaseListener() codeContainer.PackageName = packageName } - override fun enterMessageDef(ctx: Protobuf3Parser.MessageDefContext?) { + override fun enterMessageDef(ctx: Protobuf3Parser.MessageDefContext) { val codeDataStruct = constructMessageDef(ctx) codeContainer.DataStructures += codeDataStruct } - private fun constructMessageDef(ctx: Protobuf3Parser.MessageDefContext?): CodeDataStruct { - val messageName = ctx!!.messageName().text + override fun enterEnumDef(ctx: Protobuf3Parser.EnumDefContext) { + val enumDs = constructEnum(ctx) + codeContainer.DataStructures += enumDs + } + + private fun constructMessageDef(ctx: Protobuf3Parser.MessageDefContext): CodeDataStruct { + val messageName = ctx.messageName().text val codeDataStruct = CodeDataStruct( NodeName = messageName, Module = codeContainer.PackageName, @@ -29,17 +35,8 @@ class ProtobufFullIdentListener(var fileName: String) : Protobuf3BaseListener() Package = codeContainer.PackageName ) - /// since a message element will be all def - ctx.messageBody().messageElement().map { - /// : field - // | enumDef - // | messageDef - // | extendDef - // | optionStatement - // | oneof - // | mapField - // | reserved - when (val child = it.getChild(0)) { + ctx.messageBody().messageElement().map { context -> + when (val child = context.getChild(0)) { is Protobuf3Parser.FieldContext -> { codeDataStruct.Fields += CodeField( TypeType = child.type_().text, @@ -47,8 +44,11 @@ class ProtobufFullIdentListener(var fileName: String) : Protobuf3BaseListener() TypeValue = child.fieldNumber().text ) } + is Protobuf3Parser.EnumDefContext -> { + val enumDs = constructEnum(child) + codeDataStruct.InnerStructures += enumDs } is Protobuf3Parser.MessageDefContext -> { @@ -83,6 +83,36 @@ class ProtobufFullIdentListener(var fileName: String) : Protobuf3BaseListener() return codeDataStruct } + private fun constructEnum(child: Protobuf3Parser.EnumDefContext): CodeDataStruct { + val name = child.enumName().text + val enumDs = CodeDataStruct( + NodeName = name, + Type = DataStructType.ENUM, + Module = codeContainer.PackageName, + FilePath = codeContainer.FullName, + Package = codeContainer.PackageName + ) + + child.enumBody().enumElement().map { + when (val child = it.getChild(0)) { + is Protobuf3Parser.OptionStatementContext -> { + enumDs.Fields += CodeField( + + ) + } + + is Protobuf3Parser.EnumFieldContext -> { + enumDs.Fields += CodeField( + TypeType = name, + TypeKey = child.ident().text, + TypeValue = child.intLit().text + ) + } + } + } + return enumDs + } + fun getNodeInfo(): CodeContainer { return codeContainer } diff --git a/chapi-ast-protobuf/src/test/kotlin/chapi/ast/protobuf/ProtobufAnalyserTest.kt b/chapi-ast-protobuf/src/test/kotlin/chapi/ast/protobuf/ProtobufAnalyserTest.kt index 608900b4..68fcb1aa 100644 --- a/chapi-ast-protobuf/src/test/kotlin/chapi/ast/protobuf/ProtobufAnalyserTest.kt +++ b/chapi-ast-protobuf/src/test/kotlin/chapi/ast/protobuf/ProtobufAnalyserTest.kt @@ -32,4 +32,33 @@ class ProtobufAnalyserTest { assertEquals("name", field.TypeKey) assertEquals("1", field.TypeValue) } + + /// should parse for enum + @Test + fun `should parse valid protobuf code with enum and return a CodeContainer`() { + // Given + val protobufCode = "syntax = \"proto3\";\npackage example;\n\nenum PhoneType {\n MOBILE = 0;\n HOME = 1;\n WORK = 2;\n}" + val filePath = "path/to/file.proto" + val analyser = ProtobufAnalyser() + + // When + val codeContainer = analyser.analysis(protobufCode, filePath) + + // Then + assertNotNull(codeContainer) + assertEquals("example", codeContainer.PackageName) + assertTrue(codeContainer.DataStructures.isNotEmpty()) + + val dataStruct = codeContainer.DataStructures.first() + assertEquals("PhoneType", dataStruct.NodeName) + assertEquals("example", dataStruct.Module) + assertEquals("path/to/file.proto", dataStruct.FilePath) + assertEquals("example", dataStruct.Package) + assertTrue(dataStruct.Fields.isNotEmpty()) + + val field = dataStruct.Fields.first() + assertEquals("PhoneType", field.TypeType) + assertEquals("MOBILE", field.TypeKey) + assertEquals("0", field.TypeValue) + } } diff --git a/chapi-domain/src/main/kotlin/chapi/domain/core/CodeDataStruct.kt b/chapi-domain/src/main/kotlin/chapi/domain/core/CodeDataStruct.kt index e4c3d5a3..1b4f1cc7 100644 --- a/chapi-domain/src/main/kotlin/chapi/domain/core/CodeDataStruct.kt +++ b/chapi-domain/src/main/kotlin/chapi/domain/core/CodeDataStruct.kt @@ -43,6 +43,9 @@ data class CodeDataStruct( var Implements: List = listOf(), var Extend: String = "", var Functions: List = listOf(), + /** + * for Java, TypeScript, a class can have inner class, interface, enum + */ var InnerStructures: List = listOf(), var Annotations: List = listOf(), var FunctionCalls: List = listOf(),