From e2f301abf0635707fdff85464e0cceadf2819d24 Mon Sep 17 00:00:00 2001 From: Shivasurya Date: Wed, 6 Nov 2024 22:23:16 -0500 Subject: [PATCH 1/4] :beer: supported BlockStmt statement --- sourcecode-parser/graph/construct.go | 16 ++++++ .../graph/java/parse_statement.go | 10 ++++ sourcecode-parser/graph/query.go | 11 +++++ sourcecode-parser/model/stmt.go | 49 +++++++++++++++++++ 4 files changed, 86 insertions(+) diff --git a/sourcecode-parser/graph/construct.go b/sourcecode-parser/graph/construct.go index 168e3a9f..cd7c5956 100644 --- a/sourcecode-parser/graph/construct.go +++ b/sourcecode-parser/graph/construct.go @@ -55,6 +55,7 @@ type Node struct { YieldStmt *model.YieldStmt AssertStmt *model.AssertStmt ReturnStmt *model.ReturnStmt + BlockStmt *model.BlockStmt } type Edge struct { @@ -183,6 +184,21 @@ func parseJavadocTags(commentContent string) *model.Javadoc { func buildGraphFromAST(node *sitter.Node, sourceCode []byte, graph *CodeGraph, currentContext *Node, file string) { isJavaSourceFile := isJavaSourceFile(file) switch node.Type() { + case "block": + blockNode := javalang.ParseBlockStatement(node, sourceCode) + uniqueBlockID := fmt.Sprintf("block_%d_%d_%s", node.StartPoint().Row+1, node.StartPoint().Column+1, file) + blockStmtNode := &Node{ + ID: GenerateSha256(uniqueBlockID), + Type: "BlockStmt", + LineNumber: node.StartPoint().Row + 1, + Name: "BlockStmt", + IsExternal: true, + CodeSnippet: node.Content(sourceCode), + File: file, + isJavaSourceFile: isJavaSourceFile, + BlockStmt: blockNode, + } + graph.AddNode(blockStmtNode) case "return_statement": returnNode := javalang.ParseReturnStatement(node, sourceCode) uniqueReturnID := fmt.Sprintf("return_%d_%d_%s", node.StartPoint().Row+1, node.StartPoint().Column+1, file) diff --git a/sourcecode-parser/graph/java/parse_statement.go b/sourcecode-parser/graph/java/parse_statement.go index 48ef71f6..5f554e31 100644 --- a/sourcecode-parser/graph/java/parse_statement.go +++ b/sourcecode-parser/graph/java/parse_statement.go @@ -50,3 +50,13 @@ func ParseReturnStatement(node *sitter.Node, sourcecode []byte) *model.ReturnStm } return returnStmt } + +func ParseBlockStatement(node *sitter.Node, sourcecode []byte) *model.BlockStmt { + blockStmt := &model.BlockStmt{} + for i := 0; i < int(node.ChildCount()); i++ { + singleBlockStmt := &model.Stmt{} + singleBlockStmt.NodeString = node.Child(i).Content(sourcecode) + blockStmt.Stmts = append(blockStmt.Stmts, *singleBlockStmt) + } + return blockStmt +} diff --git a/sourcecode-parser/graph/query.go b/sourcecode-parser/graph/query.go index 006832d9..f287fdf9 100644 --- a/sourcecode-parser/graph/query.go +++ b/sourcecode-parser/graph/query.go @@ -155,6 +155,10 @@ func (env *Env) GetReturnStmt() *model.ReturnStmt { return env.Node.ReturnStmt } +func (env *Env) GetBlockStmt() *model.BlockStmt { + return env.Node.BlockStmt +} + func QueryEntities(graph *CodeGraph, query parser.Query) (nodes [][]*Node, output [][]interface{}) { result := make([][]*Node, 0) @@ -335,6 +339,7 @@ func generateProxyEnv(node *Node, query parser.Query) map[string]interface{} { yieldStmt := "YieldStmt" assertStmt := "AssertStmt" returnStmt := "ReturnStmt" + blockStmt := "BlockStmt" // print query select list for _, entity := range query.SelectList { @@ -401,6 +406,8 @@ func generateProxyEnv(node *Node, query parser.Query) map[string]interface{} { assertStmt = entity.Alias case "ReturnStmt": returnStmt = entity.Alias + case "BlockStmt": + blockStmt = entity.Alias } } env := map[string]interface{}{ @@ -567,6 +574,10 @@ func generateProxyEnv(node *Node, query parser.Query) map[string]interface{} { "toString": proxyenv.ToString, "getReturnStmt": proxyenv.GetReturnStmt, }, + blockStmt: map[string]interface{}{ + "toString": proxyenv.ToString, + "getBlockStmt": proxyenv.GetBlockStmt, + }, } return env } diff --git a/sourcecode-parser/model/stmt.go b/sourcecode-parser/model/stmt.go index 7ef4fdd7..e9dda347 100644 --- a/sourcecode-parser/model/stmt.go +++ b/sourcecode-parser/model/stmt.go @@ -375,3 +375,52 @@ func (returnStmt *ReturnStmt) ToString() string { func (returnStmt *ReturnStmt) GetResult() *Expr { return returnStmt.Result } + +type BlockStmt struct { + Stmt + Stmts []Stmt +} + +type IBlockStmt interface { + GetAPrimaryQlClass() string + GetHalsteadID() int + GetPP() string + ToString() string + GetStmt(index int) Stmt + GetAStmt() Stmt + GetNumStmt() int + GetLastStmt() Stmt +} + +func (blockStmt *BlockStmt) GetAPrimaryQlClass() string { + return "BlockStmt" +} + +func (blockStmt *BlockStmt) GetHalsteadID() int { + // TODO: Implement Halstead ID calculation for BlockStmt + return 0 +} + +func (blockStmt *BlockStmt) GetPP() string { + return fmt.Sprintf("block %s", blockStmt.Stmts) +} + +func (blockStmt *BlockStmt) ToString() string { + return fmt.Sprintf("block %s", blockStmt.Stmts) +} + +func (blockStmt *BlockStmt) GetStmt(index int) Stmt { + return blockStmt.Stmts[index] +} + +func (blockStmt *BlockStmt) GetAStmt() Stmt { + return blockStmt.Stmts[0] +} + +func (blockStmt *BlockStmt) GetNumStmt() int { + return len(blockStmt.Stmts) +} + +func (blockStmt *BlockStmt) GetLastStmt() Stmt { + return blockStmt.Stmts[len(blockStmt.Stmts)-1] +} From 865a3b4631d30982f82d59a12b8da8770cf80ef5 Mon Sep 17 00:00:00 2001 From: Shivasurya Date: Fri, 8 Nov 2024 14:00:56 -0500 Subject: [PATCH 2/4] :beer: Fixed testcase --- sourcecode-parser/graph/construct_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sourcecode-parser/graph/construct_test.go b/sourcecode-parser/graph/construct_test.go index 061c2013..68c1bd26 100644 --- a/sourcecode-parser/graph/construct_test.go +++ b/sourcecode-parser/graph/construct_test.go @@ -701,9 +701,9 @@ func TestBuildGraphFromAST(t *testing.T) { } } `, - expectedNodes: 3, + expectedNodes: 4, expectedEdges: 0, - expectedTypes: []string{"class_declaration", "method_declaration", "variable_declaration"}, + expectedTypes: []string{"class_declaration", "method_declaration", "variable_declaration", "BlockStmt"}, unexpectedTypes: []string{"method_invocation"}, }, { @@ -718,9 +718,9 @@ func TestBuildGraphFromAST(t *testing.T) { } } `, - expectedNodes: 5, + expectedNodes: 7, expectedEdges: 2, - expectedTypes: []string{"class_declaration", "method_declaration", "method_invocation"}, + expectedTypes: []string{"class_declaration", "method_declaration", "method_invocation", "BlockStmt"}, unexpectedTypes: []string{"variable_declaration"}, }, { @@ -732,7 +732,7 @@ func TestBuildGraphFromAST(t *testing.T) { } } `, - expectedNodes: 5, + expectedNodes: 6, expectedEdges: 0, expectedTypes: []string{"class_declaration", "method_declaration", "binary_expression", "ReturnStmt"}, unexpectedTypes: []string{"variable_declaration"}, @@ -791,9 +791,9 @@ func TestBuildGraphFromAST(t *testing.T) { } } `, - expectedNodes: 74, + expectedNodes: 83, expectedEdges: 5, - expectedTypes: []string{"class_declaration", "method_declaration", "binary_expression", "comp_expression", "and_expression", "or_expression", "IfStmt", "ForStmt", "WhileStmt", "DoStmt", "BreakStmt", "ContinueStmt", "YieldStmt", "ReturnStmt"}, + expectedTypes: []string{"class_declaration", "method_declaration", "binary_expression", "comp_expression", "and_expression", "or_expression", "IfStmt", "ForStmt", "WhileStmt", "DoStmt", "BreakStmt", "ContinueStmt", "YieldStmt", "ReturnStmt", "BlockStmt"}, unexpectedTypes: []string{""}, }, { @@ -811,7 +811,7 @@ func TestBuildGraphFromAST(t *testing.T) { } } `, - expectedNodes: 4, + expectedNodes: 5, expectedEdges: 0, expectedTypes: []string{"class_declaration", "method_declaration", "block_comment", "ReturnStmt"}, unexpectedTypes: []string{"variable_declaration", "binary_expression"}, @@ -827,7 +827,7 @@ func TestBuildGraphFromAST(t *testing.T) { } } `, - expectedNodes: 6, + expectedNodes: 7, expectedEdges: 0, expectedTypes: []string{"class_declaration", "method_declaration", "ClassInstanceExpr"}, unexpectedTypes: []string{"binary_expression"}, From 09c4c785fc222ca7182e4f724ca75c8169ab8ca2 Mon Sep 17 00:00:00 2001 From: Shivasurya Date: Sat, 9 Nov 2024 12:23:30 -0500 Subject: [PATCH 3/4] :beer: Fixed testcase --- .../graph/java/parse_statement_test.go | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/sourcecode-parser/graph/java/parse_statement_test.go b/sourcecode-parser/graph/java/parse_statement_test.go index 7b573471..16b0afdb 100644 --- a/sourcecode-parser/graph/java/parse_statement_test.go +++ b/sourcecode-parser/graph/java/parse_statement_test.go @@ -197,3 +197,83 @@ func TestParseAssertStatement(t *testing.T) { }) } } + +func TestParseBlockStatement(t *testing.T) { + tests := []struct { + name string + input string + expected *model.BlockStmt + }{ + { + name: "Empty block statement", + input: "{}", + expected: &model.BlockStmt{ + Stmts: []model.Stmt{ + {NodeString: "{"}, + {NodeString: "}"}, + }, + }, + }, + { + name: "Single statement block", + input: "{return true;}", + expected: &model.BlockStmt{ + Stmts: []model.Stmt{ + {NodeString: "{"}, + {NodeString: "return true;"}, + {NodeString: "}"}, + }, + }, + }, + { + name: "Multiple statement block", + input: "{int x = 1; x++; return x;}", + expected: &model.BlockStmt{ + Stmts: []model.Stmt{ + {NodeString: "{"}, + {NodeString: "int x = 1;"}, + {NodeString: "x++;"}, + {NodeString: "return x;"}, + {NodeString: "}"}, + }, + }, + }, + { + name: "Nested block statements", + input: "{{int x = 1;}{int y = 2;}}", + expected: &model.BlockStmt{ + Stmts: []model.Stmt{ + {NodeString: "{"}, + {NodeString: "{int x = 1;}"}, + {NodeString: "{int y = 2;}"}, + {NodeString: "}"}, + }, + }, + }, + { + name: "Block with complex statements", + input: "{System.out.println(\"Hello\"); if(x > 0) { return true; } throw new Exception();}", + expected: &model.BlockStmt{ + Stmts: []model.Stmt{ + {NodeString: "{"}, + {NodeString: "System.out.println(\"Hello\");"}, + {NodeString: "if(x > 0) { return true; }"}, + {NodeString: "throw new Exception();"}, + {NodeString: "}"}, + }, + }, + }, + } + + parser := sitter.NewParser() + parser.SetLanguage(java.GetLanguage()) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tree := parser.Parse(nil, []byte(tt.input)) + node := tree.RootNode().Child(0) + result := ParseBlockStatement(node, []byte(tt.input)) + assert.Equal(t, tt.expected, result) + }) + } +} From 0b8bbdfc1b8d329fc05db912edfe8547390d30f0 Mon Sep 17 00:00:00 2001 From: Shivasurya Date: Sat, 9 Nov 2024 12:26:55 -0500 Subject: [PATCH 4/4] :beer: Fixed testcase --- sourcecode-parser/model/stmt_test.go | 58 ++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/sourcecode-parser/model/stmt_test.go b/sourcecode-parser/model/stmt_test.go index 2a9a5465..457b04b7 100644 --- a/sourcecode-parser/model/stmt_test.go +++ b/sourcecode-parser/model/stmt_test.go @@ -428,3 +428,61 @@ func TestReturnStmt_GetPP(t *testing.T) { assert.Equal(t, "return x > 0 && y < 10", returnStmt.GetPP()) }) } + +func TestBlockStmt(t *testing.T) { + t.Run("GetAPrimaryQlClass", func(t *testing.T) { + blockStmt := &BlockStmt{} + assert.Equal(t, "BlockStmt", blockStmt.GetAPrimaryQlClass()) + }) + + t.Run("GetHalsteadID", func(t *testing.T) { + blockStmt := &BlockStmt{} + assert.Equal(t, 0, blockStmt.GetHalsteadID()) + }) + + t.Run("GetStmt with valid index", func(t *testing.T) { + stmt1 := Stmt{NodeString: "x = 1"} + stmt2 := Stmt{NodeString: "y = 2"} + blockStmt := &BlockStmt{ + Stmts: []Stmt{stmt1, stmt2}, + } + assert.Equal(t, stmt2, blockStmt.GetStmt(1)) + }) + + t.Run("GetAStmt with non-empty block", func(t *testing.T) { + stmt1 := Stmt{NodeString: "x = 1"} + stmt2 := Stmt{NodeString: "y = 2"} + blockStmt := &BlockStmt{ + Stmts: []Stmt{stmt1, stmt2}, + } + assert.Equal(t, stmt1, blockStmt.GetAStmt()) + }) + + t.Run("GetNumStmt with multiple statements", func(t *testing.T) { + blockStmt := &BlockStmt{ + Stmts: []Stmt{ + {NodeString: "x = 1"}, + {NodeString: "y = 2"}, + {NodeString: "z = 3"}, + }, + } + assert.Equal(t, 3, blockStmt.GetNumStmt()) + }) + + t.Run("GetNumStmt with empty block", func(t *testing.T) { + blockStmt := &BlockStmt{ + Stmts: []Stmt{}, + } + assert.Equal(t, 0, blockStmt.GetNumStmt()) + }) + + t.Run("GetLastStmt with multiple statements", func(t *testing.T) { + stmt1 := Stmt{NodeString: "x = 1"} + stmt2 := Stmt{NodeString: "y = 2"} + stmt3 := Stmt{NodeString: "z = 3"} + blockStmt := &BlockStmt{ + Stmts: []Stmt{stmt1, stmt2, stmt3}, + } + assert.Equal(t, stmt3, blockStmt.GetLastStmt()) + }) +}