Skip to content

Commit

Permalink
feat(protobuf-parser): enhance parsing for enum definitions and inner…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
phodal committed Oct 25, 2024
1 parent c71e51b commit 67e8578
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -14,41 +15,40 @@ 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,
FilePath = codeContainer.FullName,
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,
TypeKey = child.fieldName().text,
TypeValue = child.fieldNumber().text
)
}

is Protobuf3Parser.EnumDefContext -> {
val enumDs = constructEnum(child)

codeDataStruct.InnerStructures += enumDs
}

is Protobuf3Parser.MessageDefContext -> {
Expand Down Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ data class CodeDataStruct(
var Implements: List<String> = listOf(),
var Extend: String = "",
var Functions: List<CodeFunction> = listOf(),
/**
* for Java, TypeScript, a class can have inner class, interface, enum
*/
var InnerStructures: List<CodeDataStruct> = listOf(),
var Annotations: List<CodeAnnotation> = listOf(),
var FunctionCalls: List<CodeCall> = listOf(),
Expand Down

0 comments on commit 67e8578

Please sign in to comment.