Skip to content

Commit

Permalink
feat(go): make go better for parsing API
Browse files Browse the repository at this point in the history
  • Loading branch information
phodal committed Nov 18, 2022
1 parent ac664f6 commit 82373a2
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 35 deletions.
1 change: 1 addition & 0 deletions chapi-ast-go/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Chapi Go
86 changes: 60 additions & 26 deletions chapi-ast-go/src/main/kotlin/chapi/ast/goast/GoFullIdentListener.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand All @@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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?) {
Expand Down
35 changes: 26 additions & 9 deletions chapi-ast-go/src/test/kotlin/chapi/ast/goast/GoMethodCallTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}

0 comments on commit 82373a2

Please sign in to comment.