From 82373a2fec0fca26a456f47447828a5e1616a95a Mon Sep 17 00:00:00 2001 From: Phodal Huang Date: Fri, 18 Nov 2022 10:48:33 +0800 Subject: [PATCH] feat(go): make go better for parsing API --- chapi-ast-go/README.md | 1 + .../chapi/ast/goast/GoFullIdentListener.kt | 86 +++++++++++++------ .../chapi/ast/goast/GoMethodCallTest.kt | 35 ++++++-- 3 files changed, 87 insertions(+), 35 deletions(-) create mode 100644 chapi-ast-go/README.md diff --git a/chapi-ast-go/README.md b/chapi-ast-go/README.md new file mode 100644 index 00000000..3783621e --- /dev/null +++ b/chapi-ast-go/README.md @@ -0,0 +1 @@ +# Chapi Go diff --git a/chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoFullIdentListener.kt b/chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoFullIdentListener.kt index 056e7b47..c9f5e4c0 100644 --- a/chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoFullIdentListener.kt +++ b/chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoFullIdentListener.kt @@ -2,8 +2,12 @@ package chapi.ast.goast import chapi.ast.antlr.GoParser import chapi.domain.core.* +import org.antlr.v4.runtime.tree.ParseTree import org.antlr.v4.runtime.tree.TerminalNodeImpl +/** + * core logic, please goto [GoFullIdentListener.enterExpression] to see how to use it + */ class GoFullIdentListener(var fileName: String) : GoAstListener() { private var codeContainer: CodeContainer = CodeContainer(FullName = fileName) @@ -120,17 +124,26 @@ class GoFullIdentListener(var fileName: String) : GoAstListener() { }.toTypedArray() } + /** + * !!!IMPORTANT + * lookup all primaryExpr, then find the method call, + * examples: + * 1. http.Status will be Primary Dot IDENTIFIER + * 2. http.Status() will be Primary Dot arguments + * 3. http[0] will be Primary Index + * So, we just look up expression, the find the primaryExpr, will handle all cases + * expression -> primaryExpr -> PrimaryExpr, then handle [GoFullIdentListener.handlePrimaryExprCtx] + */ override fun enterExpression(ctx: GoParser.ExpressionContext?) { - when (val firstChild = ctx!!.getChild(0)) { + when (val firstChild = ctx?.getChild(0)) { is GoParser.PrimaryExprContext -> { - firstChild.getChild(1)?.let { - this.parsePrimaryExprCtx(firstChild) - } +// process + firstChild.getChild(1)?.let { this.handlePrimaryExprCtx(firstChild) } } } } - private fun parsePrimaryExprCtx(primaryExprCtx: GoParser.PrimaryExprContext) { + private fun handlePrimaryExprCtx(primaryExprCtx: GoParser.PrimaryExprContext) { when (val child = primaryExprCtx.getChild(1)) { is GoParser.ArgumentsContext -> { val codeCall = codeCallFromExprList(primaryExprCtx) @@ -141,14 +154,14 @@ class GoFullIdentListener(var fileName: String) : GoAstListener() { } else -> { - println("${child.javaClass} not implemented ${child.text}") +// println("${child.javaClass} not implemented ${child.text}") } } } /** - * 1. lookup local vars - * 2. lookup imports + * 1. lookup local vars by [GoFullIdentListener.localVars] + * 2. lookup imports by [CodeContainer.Imports] */ private fun wrapTarget(nodeName: String): String { codeContainer.Imports.forEach { @@ -180,20 +193,8 @@ class GoFullIdentListener(var fileName: String) : GoAstListener() { CodeCall(NodeName = child.text) when (child.getChild(1)) { is TerminalNodeImpl -> { - // primaryExpr '.' IDENTIFIER - val nodeName = when (val first = child.getChild(0)) { - is GoParser.OperandContext -> { - first.text - } - - is GoParser.PrimaryExprContext -> { - localVars.getOrDefault(first.text, first.text) - } - - else -> { - first.text - } - } + // TerminalNodeImpl => primaryExpr '.' IDENTIFIER + val nodeName = nodeNameFromPrimary(child) CodeCall( NodeName = nodeName, @@ -214,19 +215,52 @@ class GoFullIdentListener(var fileName: String) : GoAstListener() { } } + private fun nodeNameFromPrimary(child: GoParser.PrimaryExprContext): String { + val nodeName = when (val first = child.getChild(0)) { + is GoParser.OperandContext -> { + first.text + } + + is GoParser.PrimaryExprContext -> { + if (first.primaryExpr() != null) { + nodeNameFromPrimary(first) + } else { + localVars.getOrDefault(first.text, first.text) + } + } + + else -> { + first.text + } + } + return nodeName + } + override fun enterVarDecl(ctx: GoParser.VarDeclContext?) { - ctx?.varSpec()?.forEach { varSpec -> - varSpec.identifierList().IDENTIFIER().forEach { terminalNode -> - localVars[terminalNode.text] = varSpec.type_()?.text ?: "" + ctx?.varSpec()?.forEach { + it.identifierList().IDENTIFIER().forEach { terminalNode -> + localVars[terminalNode.text] = it.type_()?.text ?: "" } } } override fun enterShortVarDecl(ctx: GoParser.ShortVarDeclContext?) { ctx?.identifierList()?.IDENTIFIER()?.forEach { - localVars[it.text] = ctx.expressionList()?.text ?: "" + localVars[it.text] = nodeNameFromExpr(ctx) + } + } + + private fun nodeNameFromExpr(ctx: GoParser.ShortVarDeclContext): String { + ctx.expressionList().expression()?.forEach { + when (val firstChild = it.getChild(0)) { + is GoParser.PrimaryExprContext -> { + return nodeNameFromPrimary(firstChild) + } + } } + + return "" } override fun enterConstDecl(ctx: GoParser.ConstDeclContext?) { diff --git a/chapi-ast-go/src/test/kotlin/chapi/ast/goast/GoMethodCallTest.kt b/chapi-ast-go/src/test/kotlin/chapi/ast/goast/GoMethodCallTest.kt index 397df727..d2987287 100644 --- a/chapi-ast-go/src/test/kotlin/chapi/ast/goast/GoMethodCallTest.kt +++ b/chapi-ast-go/src/test/kotlin/chapi/ast/goast/GoMethodCallTest.kt @@ -28,14 +28,31 @@ func main() { assertEquals(codeFile.DataStructures[0].Functions.size, 1) assertEquals(codeFile.DataStructures[0].Functions[0].FunctionCalls.size, 4) - assertEquals(codeFile.DataStructures[0].Functions[0].FunctionCalls[0].NodeName, "gin") - assertEquals(codeFile.DataStructures[0].Functions[0].FunctionCalls[0].FunctionName, "Default") - assertEquals(codeFile.DataStructures[0].Functions[0].FunctionCalls[0].Package, "github.com/gin-gonic/gin") - - assertEquals(codeFile.DataStructures[0].Functions[0].FunctionCalls[1].NodeName, "gin.Default()") - assertEquals(codeFile.DataStructures[0].Functions[0].FunctionCalls[2].NodeName, "c") - assertEquals(codeFile.DataStructures[0].Functions[0].FunctionCalls[2].Parameters.size, 2) - assertEquals(codeFile.DataStructures[0].Functions[0].FunctionCalls[3].NodeName, "gin.Default()") - assertEquals(codeFile.DataStructures[0].Functions[0].FunctionCalls[3].FunctionName, "Run") + val rInitCall = codeFile.DataStructures[0].Functions[0].FunctionCalls[0] + assertEquals(rInitCall.NodeName, "gin") + assertEquals(rInitCall.FunctionName, "Default") + assertEquals(rInitCall.Package, "github.com/gin-gonic/gin") + + val rGetCall = codeFile.DataStructures[0].Functions[0].FunctionCalls[1] + assertEquals(rGetCall.NodeName, "gin") + assertEquals(rGetCall.FunctionName, "GET") + assertEquals(rGetCall.Parameters.size, 2) + assertEquals(rGetCall.Parameters[0].TypeValue, "\"/\"") + assertEquals(rGetCall.Parameters[1].TypeValue, "func(c*gin.Context){c.String(http.StatusOK,\"hello world\")\n" + + "}") + + assertEquals(rGetCall.Package, "github.com/gin-gonic/gin") + + val callback = codeFile.DataStructures[0].Functions[0].FunctionCalls[2] + assertEquals(callback.NodeName, "c") + assertEquals(callback.FunctionName, "String") + assertEquals(callback.Parameters.size, 2) + assertEquals(callback.Parameters[0].TypeValue, "http.StatusOK") + assertEquals(callback.Parameters[1].TypeValue, "\"hello world\"") + + val rRun = codeFile.DataStructures[0].Functions[0].FunctionCalls[3] + assertEquals(rRun.NodeName, "gin") + assertEquals(rRun.FunctionName, "Run") + assertEquals(rRun.Package, "github.com/gin-gonic/gin") } }