From 352e389805120b83ba3306671f66fe11fe7250bb Mon Sep 17 00:00:00 2001 From: Shivasurya Date: Mon, 4 Nov 2024 14:10:22 -0500 Subject: [PATCH] :whale: added ContinueStmt support --- sourcecode-parser/graph/construct.go | 18 ++++- sourcecode-parser/graph/construct_test.go | 6 +- .../graph/java/parse_statement.go | 11 +++ .../graph/java/parse_statement_test.go | 41 +++++++++++ sourcecode-parser/graph/query.go | 11 +++ sourcecode-parser/model/stmt.go | 39 ++++++++++ sourcecode-parser/model/stmt_test.go | 72 +++++++++++++++++++ .../com/ivb/udacity/movieDetailActivity.java | 2 +- 8 files changed, 196 insertions(+), 4 deletions(-) diff --git a/sourcecode-parser/graph/construct.go b/sourcecode-parser/graph/construct.go index da5adc3..2e23dce 100644 --- a/sourcecode-parser/graph/construct.go +++ b/sourcecode-parser/graph/construct.go @@ -51,7 +51,8 @@ type Node struct { DoStmt *model.DoStmt ForStmt *model.ForStmt BreakStmt *model.BreakStmt -} + ContinueStmt *model.ContinueStmt +} // type Edge struct { From *Node @@ -194,6 +195,21 @@ func buildGraphFromAST(node *sitter.Node, sourceCode []byte, graph *CodeGraph, c BreakStmt: breakNode, } graph.AddNode(breakStmtNode) + case "continue_statement": + continueNode := javalang.ParseContinueStatement(node, sourceCode) + uniquecontinueID := fmt.Sprintf("continuestmt_%d_%d_%s", node.StartPoint().Row+1, node.StartPoint().Column+1, file) + continueStmtNode := &Node{ + ID: GenerateSha256(uniquecontinueID), + Type: "ContinueStmt", + LineNumber: node.StartPoint().Row + 1, + Name: "ContinueStmt", + IsExternal: true, + CodeSnippet: node.Content(sourceCode), + File: file, + isJavaSourceFile: isJavaSourceFile, + ContinueStmt: continueNode, + } + graph.AddNode(continueStmtNode) case "if_statement": ifNode := model.IfStmt{} // get the condition of the if statement diff --git a/sourcecode-parser/graph/construct_test.go b/sourcecode-parser/graph/construct_test.go index 01fd0fc..43b19bd 100644 --- a/sourcecode-parser/graph/construct_test.go +++ b/sourcecode-parser/graph/construct_test.go @@ -758,6 +758,8 @@ func TestBuildGraphFromAST(t *testing.T) { a--; if (a == 0) { break outerlabel; + } else { + continue outerlabel; } } for (int i = 0; i < 10; i++) { @@ -776,9 +778,9 @@ func TestBuildGraphFromAST(t *testing.T) { } } `, - expectedNodes: 68, + expectedNodes: 69, expectedEdges: 4, - expectedTypes: []string{"class_declaration", "method_declaration", "binary_expression", "comp_expression", "and_expression", "or_expression", "IfStmt", "ForStmt", "WhileStmt", "DoStmt", "BreakStmt"}, + expectedTypes: []string{"class_declaration", "method_declaration", "binary_expression", "comp_expression", "and_expression", "or_expression", "IfStmt", "ForStmt", "WhileStmt", "DoStmt", "BreakStmt", "ContinueStmt"}, unexpectedTypes: []string{""}, }, { diff --git a/sourcecode-parser/graph/java/parse_statement.go b/sourcecode-parser/graph/java/parse_statement.go index 7155f4f..d25acf4 100644 --- a/sourcecode-parser/graph/java/parse_statement.go +++ b/sourcecode-parser/graph/java/parse_statement.go @@ -15,3 +15,14 @@ func ParseBreakStatement(node *sitter.Node, sourcecode []byte) *model.BreakStmt } return breakStmt } + +func ParseContinueStatement(node *sitter.Node, sourcecode []byte) *model.ContinueStmt { + continueStmt := &model.ContinueStmt{} + // get identifier if present child + for i := 0; i < int(node.ChildCount()); i++ { + if node.Child(i).Type() == "identifier" { + continueStmt.Label = node.Child(i).Content(sourcecode) + } + } + return continueStmt +} diff --git a/sourcecode-parser/graph/java/parse_statement_test.go b/sourcecode-parser/graph/java/parse_statement_test.go index a3f6cb5..05a59e0 100644 --- a/sourcecode-parser/graph/java/parse_statement_test.go +++ b/sourcecode-parser/graph/java/parse_statement_test.go @@ -39,3 +39,44 @@ func TestParseBreakStatement(t *testing.T) { }) } } + +func TestParseContinueStatement(t *testing.T) { + tests := []struct { + name string + input string + expected *model.ContinueStmt + }{ + { + name: "Simple continue statement without label", + input: "continue;", + expected: &model.ContinueStmt{Label: ""}, + }, + { + name: "Continue statement with label", + input: "continue outerLoop;", + expected: &model.ContinueStmt{Label: "outerLoop"}, + }, + { + name: "Continue statement with complex label", + input: "continue COMPLEX_LABEL_123;", + expected: &model.ContinueStmt{Label: "COMPLEX_LABEL_123"}, + }, + { + name: "Continue statement with underscore label", + input: "continue outer_loop_label;", + expected: &model.ContinueStmt{Label: "outer_loop_label"}, + }, + } + + 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 := ParseContinueStatement(node, []byte(tt.input)) + assert.Equal(t, tt.expected, result) + }) + } +} diff --git a/sourcecode-parser/graph/query.go b/sourcecode-parser/graph/query.go index fbfd7de..8e20535 100644 --- a/sourcecode-parser/graph/query.go +++ b/sourcecode-parser/graph/query.go @@ -139,6 +139,10 @@ func (env *Env) GetBreakStmt() *model.BreakStmt { return env.Node.BreakStmt } +func (env *Env) GetContinueStmt() *model.ContinueStmt { + return env.Node.ContinueStmt +} + func QueryEntities(graph *CodeGraph, query parser.Query) (nodes [][]*Node, output [][]interface{}) { result := make([][]*Node, 0) @@ -315,6 +319,7 @@ func generateProxyEnv(node *Node, query parser.Query) map[string]interface{} { doStmt := "DoStmt" forStmt := "ForStmt" breakStmt := "BreakStmt" + continueStmt := "ContinueStmt" // print query select list for _, entity := range query.SelectList { @@ -373,6 +378,8 @@ func generateProxyEnv(node *Node, query parser.Query) map[string]interface{} { forStmt = entity.Alias case "BreakStmt": breakStmt = entity.Alias + case "ContinueStmt": + continueStmt = entity.Alias } } env := map[string]interface{}{ @@ -523,6 +530,10 @@ func generateProxyEnv(node *Node, query parser.Query) map[string]interface{} { "toString": proxyenv.ToString, "getBreakStmt": proxyenv.GetBreakStmt, }, + continueStmt: map[string]interface{}{ + "toString": proxyenv.ToString, + "getContinueStmt": proxyenv.GetContinueStmt, + }, } return env } diff --git a/sourcecode-parser/model/stmt.go b/sourcecode-parser/model/stmt.go index fe77505..461deb9 100644 --- a/sourcecode-parser/model/stmt.go +++ b/sourcecode-parser/model/stmt.go @@ -227,3 +227,42 @@ func (breakStmt *BreakStmt) hasLabel() bool { func (breakStmt *BreakStmt) GetLabel() string { return breakStmt.Label } + +type IContinueStmt interface { + GetAPrimaryQlClass() string + GetHalsteadID() int + GetLabel() string + hasLabel() bool + GetPP() string + ToString() string +} + +type ContinueStmt struct { + JumpStmt + Label string +} + +func (continueStmt *ContinueStmt) GetAPrimaryQlClass() string { + return "ContinueStmt" +} + +func (continueStmt *ContinueStmt) GetHalsteadID() int { + // TODO: Implement Halstead ID calculation for ContinueStmt + return 0 +} + +func (continueStmt *ContinueStmt) GetPP() string { + return fmt.Sprintf("continue (%s)", continueStmt.Label) +} + +func (continueStmt *ContinueStmt) ToString() string { + return fmt.Sprintf("continue (%s)", continueStmt.Label) +} + +func (continueStmt *ContinueStmt) hasLabel() bool { + return continueStmt.Label != "" +} + +func (continueStmt *ContinueStmt) GetLabel() string { + return continueStmt.Label +} diff --git a/sourcecode-parser/model/stmt_test.go b/sourcecode-parser/model/stmt_test.go index 055fa9a..af28a0e 100644 --- a/sourcecode-parser/model/stmt_test.go +++ b/sourcecode-parser/model/stmt_test.go @@ -147,3 +147,75 @@ func TestIfStmt(t *testing.T) { assert.Equal(t, expected, ifStmt.ToString()) }) } + +func TestContinueStmt(t *testing.T) { + t.Run("GetAPrimaryQlClass", func(t *testing.T) { + continueStmt := &ContinueStmt{} + assert.Equal(t, "ContinueStmt", continueStmt.GetAPrimaryQlClass()) + }) + + t.Run("GetHalsteadID", func(t *testing.T) { + continueStmt := &ContinueStmt{} + assert.Equal(t, 0, continueStmt.GetHalsteadID()) + }) + + t.Run("GetPP with label", func(t *testing.T) { + continueStmt := &ContinueStmt{ + Label: "outerLoop", + } + expected := "continue (outerLoop)" + assert.Equal(t, expected, continueStmt.GetPP()) + }) + + t.Run("GetPP without label", func(t *testing.T) { + continueStmt := &ContinueStmt{ + Label: "", + } + expected := "continue ()" + assert.Equal(t, expected, continueStmt.GetPP()) + }) + + t.Run("ToString with label", func(t *testing.T) { + continueStmt := &ContinueStmt{ + Label: "innerLoop", + } + expected := "continue (innerLoop)" + assert.Equal(t, expected, continueStmt.ToString()) + }) + + t.Run("ToString without label", func(t *testing.T) { + continueStmt := &ContinueStmt{ + Label: "", + } + expected := "continue ()" + assert.Equal(t, expected, continueStmt.ToString()) + }) + + t.Run("hasLabel with label", func(t *testing.T) { + continueStmt := &ContinueStmt{ + Label: "loop1", + } + assert.True(t, continueStmt.hasLabel()) + }) + + t.Run("hasLabel without label", func(t *testing.T) { + continueStmt := &ContinueStmt{ + Label: "", + } + assert.False(t, continueStmt.hasLabel()) + }) + + t.Run("GetLabel with label", func(t *testing.T) { + continueStmt := &ContinueStmt{ + Label: "loop2", + } + assert.Equal(t, "loop2", continueStmt.GetLabel()) + }) + + t.Run("GetLabel without label", func(t *testing.T) { + continueStmt := &ContinueStmt{ + Label: "", + } + assert.Equal(t, "", continueStmt.GetLabel()) + }) +} diff --git a/test-src/android/app/src/main/java/com/ivb/udacity/movieDetailActivity.java b/test-src/android/app/src/main/java/com/ivb/udacity/movieDetailActivity.java index c6e9466..ffc410a 100644 --- a/test-src/android/app/src/main/java/com/ivb/udacity/movieDetailActivity.java +++ b/test-src/android/app/src/main/java/com/ivb/udacity/movieDetailActivity.java @@ -53,7 +53,7 @@ protected void onCreate(Bundle savedInstanceState) { if (savedInstanceState == null) { movieDetailFragment fragment = new movieDetailFragment(); - break outlabel; + continue outlabel; fragment.setMovieData(moviegeneralModal); getSupportFragmentManager().beginTransaction() .add(R.id.movie_detail_container, fragment)