From d8bbf0c6f73b20592a60dfabb29a1acbee05f814 Mon Sep 17 00:00:00 2001 From: Shivasurya Date: Mon, 4 Nov 2024 12:36:15 -0500 Subject: [PATCH] =?UTF-8?q?feature:=20=F0=9F=8D=BA=20Support=20for=20=20`B?= =?UTF-8?q?reakStmt`=20(#174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :whale: added support * :whale: added support --- sourcecode-parser/graph/construct.go | 18 ++++++ sourcecode-parser/graph/construct_test.go | 9 ++- .../graph/java/parse_statement.go | 17 ++++++ .../graph/java/parse_statement_test.go | 41 +++++++++++++ sourcecode-parser/graph/query.go | 11 ++++ sourcecode-parser/model/stmt.go | 61 +++++++++++++++++++ .../com/ivb/udacity/movieDetailActivity.java | 3 +- 7 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 sourcecode-parser/graph/java/parse_statement.go create mode 100644 sourcecode-parser/graph/java/parse_statement_test.go diff --git a/sourcecode-parser/graph/construct.go b/sourcecode-parser/graph/construct.go index 449a2e8..da5adc3 100644 --- a/sourcecode-parser/graph/construct.go +++ b/sourcecode-parser/graph/construct.go @@ -10,6 +10,8 @@ import ( "sync" "time" + javalang "github.com/shivasurya/code-pathfinder/sourcecode-parser/graph/java" + "github.com/shivasurya/code-pathfinder/sourcecode-parser/model" "github.com/smacker/go-tree-sitter/java" @@ -48,6 +50,7 @@ type Node struct { WhileStmt *model.WhileStmt DoStmt *model.DoStmt ForStmt *model.ForStmt + BreakStmt *model.BreakStmt } type Edge struct { @@ -176,6 +179,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 "break_statement": + breakNode := javalang.ParseBreakStatement(node, sourceCode) + uniquebreakstmtID := fmt.Sprintf("breakstmt_%d_%d_%s", node.StartPoint().Row+1, node.StartPoint().Column+1, file) + breakStmtNode := &Node{ + ID: GenerateSha256(uniquebreakstmtID), + Type: "BreakStmt", + LineNumber: node.StartPoint().Row + 1, + Name: "BreakStmt", + IsExternal: true, + CodeSnippet: node.Content(sourceCode), + File: file, + isJavaSourceFile: isJavaSourceFile, + BreakStmt: breakNode, + } + graph.AddNode(breakStmtNode) 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 ae1f350..01fd0fc 100644 --- a/sourcecode-parser/graph/construct_test.go +++ b/sourcecode-parser/graph/construct_test.go @@ -753,11 +753,16 @@ func TestBuildGraphFromAST(t *testing.T) { int i = 1 | 1; int j = 1 ^ 1; int l = 1 >>> 1; + outerlabel: while (a > 0) { a--; + if (a == 0) { + break outerlabel; + } } for (int i = 0; i < 10; i++) { System.out.println(i); + break; } do { System.out.println("Hello, World!"); @@ -771,9 +776,9 @@ func TestBuildGraphFromAST(t *testing.T) { } } `, - expectedNodes: 63, + expectedNodes: 68, expectedEdges: 4, - expectedTypes: []string{"class_declaration", "method_declaration", "binary_expression", "comp_expression", "and_expression", "or_expression", "IfStmt", "ForStmt", "WhileStmt", "DoStmt"}, + expectedTypes: []string{"class_declaration", "method_declaration", "binary_expression", "comp_expression", "and_expression", "or_expression", "IfStmt", "ForStmt", "WhileStmt", "DoStmt", "BreakStmt"}, unexpectedTypes: []string{""}, }, { diff --git a/sourcecode-parser/graph/java/parse_statement.go b/sourcecode-parser/graph/java/parse_statement.go new file mode 100644 index 0000000..7155f4f --- /dev/null +++ b/sourcecode-parser/graph/java/parse_statement.go @@ -0,0 +1,17 @@ +package java + +import ( + "github.com/shivasurya/code-pathfinder/sourcecode-parser/model" + sitter "github.com/smacker/go-tree-sitter" +) + +func ParseBreakStatement(node *sitter.Node, sourcecode []byte) *model.BreakStmt { + breakStmt := &model.BreakStmt{} + // get identifier if present child + for i := 0; i < int(node.ChildCount()); i++ { + if node.Child(i).Type() == "identifier" { + breakStmt.Label = node.Child(i).Content(sourcecode) + } + } + return breakStmt +} diff --git a/sourcecode-parser/graph/java/parse_statement_test.go b/sourcecode-parser/graph/java/parse_statement_test.go new file mode 100644 index 0000000..a3f6cb5 --- /dev/null +++ b/sourcecode-parser/graph/java/parse_statement_test.go @@ -0,0 +1,41 @@ +package java + +import ( + "github.com/smacker/go-tree-sitter/java" + "testing" + + "github.com/shivasurya/code-pathfinder/sourcecode-parser/model" + sitter "github.com/smacker/go-tree-sitter" + "github.com/stretchr/testify/assert" +) + +func TestParseBreakStatement(t *testing.T) { + tests := []struct { + name string + input string + expected *model.BreakStmt + }{ + { + name: "Simple break statement without label", + input: "break;", + expected: &model.BreakStmt{Label: ""}, + }, + { + name: "Break statement with label", + input: "break myLabel;", + expected: &model.BreakStmt{Label: "myLabel"}, + }, + } + + 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 := ParseBreakStatement(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 6fd409c..fbfd7de 100644 --- a/sourcecode-parser/graph/query.go +++ b/sourcecode-parser/graph/query.go @@ -135,6 +135,10 @@ func (env *Env) GetForStmt() *model.ForStmt { return env.Node.ForStmt } +func (env *Env) GetBreakStmt() *model.BreakStmt { + return env.Node.BreakStmt +} + func QueryEntities(graph *CodeGraph, query parser.Query) (nodes [][]*Node, output [][]interface{}) { result := make([][]*Node, 0) @@ -310,6 +314,7 @@ func generateProxyEnv(node *Node, query parser.Query) map[string]interface{} { whileStmt := "WhileStmt" doStmt := "DoStmt" forStmt := "ForStmt" + breakStmt := "BreakStmt" // print query select list for _, entity := range query.SelectList { @@ -366,6 +371,8 @@ func generateProxyEnv(node *Node, query parser.Query) map[string]interface{} { doStmt = entity.Alias case "ForStmt": forStmt = entity.Alias + case "BreakStmt": + breakStmt = entity.Alias } } env := map[string]interface{}{ @@ -512,6 +519,10 @@ func generateProxyEnv(node *Node, query parser.Query) map[string]interface{} { "getForStmt": proxyenv.GetForStmt, "toString": proxyenv.ToString, }, + breakStmt: map[string]interface{}{ + "toString": proxyenv.ToString, + "getBreakStmt": proxyenv.GetBreakStmt, + }, } return env } diff --git a/sourcecode-parser/model/stmt.go b/sourcecode-parser/model/stmt.go index fddae4f..fe77505 100644 --- a/sourcecode-parser/model/stmt.go +++ b/sourcecode-parser/model/stmt.go @@ -166,3 +166,64 @@ func (whileStmt *WhileStmt) GetPP() string { func (whileStmt *WhileStmt) ToString() string { return fmt.Sprintf("while (%s) %s", whileStmt.Condition.NodeString, whileStmt.Stmt.NodeString) } + +type ILabeledStmt interface { + GetAPrimaryQlClass() string + GetHalsteadID() int + GetLabel() *LabeledStmt + GetPP() string + ToString() string +} + +type LabeledStmt struct { + Stmt + Label *LabeledStmt +} + +type JumpStmt struct { + Stmt +} + +type IJumpStmt interface { + GetTarget() *StmtParent + GetTargetLabel() *LabeledStmt +} + +type IBreakStmt interface { + GetAPrimaryQlClass() string + GetHalsteadID() int + GetLabel() string + hasLabel() bool + GetPP() string + ToString() string +} + +type BreakStmt struct { + JumpStmt + Label string +} + +func (breakStmt *BreakStmt) GetAPrimaryQlClass() string { + return "BreakStmt" +} + +func (breakStmt *BreakStmt) GetHalsteadID() int { + // TODO: Implement Halstead ID calculation for BreakStmt + return 0 +} + +func (breakStmt *BreakStmt) GetPP() string { + return fmt.Sprintf("break (%s)", breakStmt.Label) +} + +func (breakStmt *BreakStmt) ToString() string { + return fmt.Sprintf("break (%s)", breakStmt.Label) +} + +func (breakStmt *BreakStmt) hasLabel() bool { + return breakStmt.Label != "" +} + +func (breakStmt *BreakStmt) GetLabel() string { + return breakStmt.Label +} 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 3d8763b..c6e9466 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 @@ -49,10 +49,11 @@ protected void onCreate(Bundle savedInstanceState) { ServerSocket serverSocket = new ServerSocket(80); movieGeneralModal moviegeneralModal = (movieGeneralModal) intent.getSerializableExtra("DATA_MOVIE"); - + outlabel: if (savedInstanceState == null) { movieDetailFragment fragment = new movieDetailFragment(); + break outlabel; fragment.setMovieData(moviegeneralModal); getSupportFragmentManager().beginTransaction() .add(R.id.movie_detail_container, fragment)