Skip to content

Commit 776da67

Browse files
authored
feature: 🍺 Support for YieldStmt statement (#176)
* 🐳 added YieldStmt support * 🐛 fix lint * ⛵ add test coverage
1 parent 67efb23 commit 776da67

File tree

8 files changed

+284
-4
lines changed

8 files changed

+284
-4
lines changed

sourcecode-parser/graph/construct.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ type Node struct {
5252
ForStmt *model.ForStmt
5353
BreakStmt *model.BreakStmt
5454
ContinueStmt *model.ContinueStmt
55-
} //
55+
YieldStmt *model.YieldStmt
56+
}
5657

5758
type Edge struct {
5859
From *Node
@@ -180,6 +181,21 @@ func parseJavadocTags(commentContent string) *model.Javadoc {
180181
func buildGraphFromAST(node *sitter.Node, sourceCode []byte, graph *CodeGraph, currentContext *Node, file string) {
181182
isJavaSourceFile := isJavaSourceFile(file)
182183
switch node.Type() {
184+
case "yield_statement":
185+
yieldNode := javalang.ParseYieldStatement(node, sourceCode)
186+
uniqueyieldID := fmt.Sprintf("yield_%d_%d_%s", node.StartPoint().Row+1, node.StartPoint().Column+1, file)
187+
yieldStmtNode := &Node{
188+
ID: GenerateSha256(uniqueyieldID),
189+
Type: "YieldStmt",
190+
LineNumber: node.StartPoint().Row + 1,
191+
Name: "YieldStmt",
192+
IsExternal: true,
193+
CodeSnippet: node.Content(sourceCode),
194+
File: file,
195+
isJavaSourceFile: isJavaSourceFile,
196+
YieldStmt: yieldNode,
197+
}
198+
graph.AddNode(yieldStmtNode)
183199
case "break_statement":
184200
breakNode := javalang.ParseBreakStatement(node, sourceCode)
185201
uniquebreakstmtID := fmt.Sprintf("breakstmt_%d_%d_%s", node.StartPoint().Row+1, node.StartPoint().Column+1, file)

sourcecode-parser/graph/construct_test.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,19 @@ func TestBuildGraphFromAST(t *testing.T) {
766766
System.out.println(i);
767767
break;
768768
}
769+
switch (day) {
770+
case "MONDAY" -> 1;
771+
case "TUESDAY" -> 2;
772+
case "WEDNESDAY" -> 3;
773+
case "THURSDAY" -> 4;
774+
case "FRIDAY" -> 5;
775+
case "SATURDAY" -> 6;
776+
case "SUNDAY" -> 7;
777+
default -> {
778+
System.out.println("Invalid day: " + day);
779+
yield 9; // Using 'yield' to return a value from this case
780+
}
781+
};
769782
do {
770783
System.out.println("Hello, World!");
771784
} while (a > 0);
@@ -778,9 +791,9 @@ func TestBuildGraphFromAST(t *testing.T) {
778791
}
779792
}
780793
`,
781-
expectedNodes: 69,
782-
expectedEdges: 4,
783-
expectedTypes: []string{"class_declaration", "method_declaration", "binary_expression", "comp_expression", "and_expression", "or_expression", "IfStmt", "ForStmt", "WhileStmt", "DoStmt", "BreakStmt", "ContinueStmt"},
794+
expectedNodes: 73,
795+
expectedEdges: 5,
796+
expectedTypes: []string{"class_declaration", "method_declaration", "binary_expression", "comp_expression", "and_expression", "or_expression", "IfStmt", "ForStmt", "WhileStmt", "DoStmt", "BreakStmt", "ContinueStmt", "YieldStmt"},
784797
unexpectedTypes: []string{""},
785798
},
786799
{

sourcecode-parser/graph/java/parse_statement.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,10 @@ func ParseContinueStatement(node *sitter.Node, sourcecode []byte) *model.Continu
2626
}
2727
return continueStmt
2828
}
29+
30+
func ParseYieldStatement(node *sitter.Node, sourcecode []byte) *model.YieldStmt {
31+
yieldStmt := &model.YieldStmt{}
32+
yieldStmtExpr := &model.Expr{NodeString: node.Child(1).Content(sourcecode)}
33+
yieldStmt.Value = yieldStmtExpr
34+
return yieldStmt
35+
}

sourcecode-parser/graph/java/parse_statement_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,59 @@ func TestParseContinueStatement(t *testing.T) {
8080
})
8181
}
8282
}
83+
84+
func TestParseYieldStatement(t *testing.T) {
85+
tests := []struct {
86+
name string
87+
input string
88+
expected *model.YieldStmt
89+
}{
90+
{
91+
name: "Simple yield statement with literal",
92+
input: "yield 42;",
93+
expected: &model.YieldStmt{
94+
Value: &model.Expr{NodeString: "42"},
95+
},
96+
},
97+
{
98+
name: "Yield statement with variable",
99+
input: "yield result;",
100+
expected: &model.YieldStmt{
101+
Value: &model.Expr{NodeString: "result"},
102+
},
103+
},
104+
{
105+
name: "Yield statement with expression",
106+
input: "yield a + b;",
107+
expected: &model.YieldStmt{
108+
Value: &model.Expr{NodeString: "a + b"},
109+
},
110+
},
111+
{
112+
name: "Yield statement with method call",
113+
input: "yield getValue();",
114+
expected: &model.YieldStmt{
115+
Value: &model.Expr{NodeString: "getValue()"},
116+
},
117+
},
118+
{
119+
name: "Yield statement with string literal",
120+
input: "yield \"hello\";",
121+
expected: &model.YieldStmt{
122+
Value: &model.Expr{NodeString: "\"hello\""},
123+
},
124+
},
125+
}
126+
127+
parser := sitter.NewParser()
128+
parser.SetLanguage(java.GetLanguage())
129+
130+
for _, tt := range tests {
131+
t.Run(tt.name, func(t *testing.T) {
132+
tree := parser.Parse(nil, []byte(tt.input))
133+
node := tree.RootNode().Child(0)
134+
result := ParseYieldStatement(node, []byte(tt.input))
135+
assert.Equal(t, tt.expected, result)
136+
})
137+
}
138+
}

sourcecode-parser/graph/query.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ func (env *Env) GetContinueStmt() *model.ContinueStmt {
143143
return env.Node.ContinueStmt
144144
}
145145

146+
func (env *Env) GetYieldStmt() *model.YieldStmt {
147+
return env.Node.YieldStmt
148+
}
149+
146150
func QueryEntities(graph *CodeGraph, query parser.Query) (nodes [][]*Node, output [][]interface{}) {
147151
result := make([][]*Node, 0)
148152

@@ -320,6 +324,7 @@ func generateProxyEnv(node *Node, query parser.Query) map[string]interface{} {
320324
forStmt := "ForStmt"
321325
breakStmt := "BreakStmt"
322326
continueStmt := "ContinueStmt"
327+
yieldStmt := "YieldStmt"
323328

324329
// print query select list
325330
for _, entity := range query.SelectList {
@@ -380,6 +385,8 @@ func generateProxyEnv(node *Node, query parser.Query) map[string]interface{} {
380385
breakStmt = entity.Alias
381386
case "ContinueStmt":
382387
continueStmt = entity.Alias
388+
case "YieldStmt":
389+
yieldStmt = entity.Alias
383390
}
384391
}
385392
env := map[string]interface{}{
@@ -534,6 +541,10 @@ func generateProxyEnv(node *Node, query parser.Query) map[string]interface{} {
534541
"toString": proxyenv.ToString,
535542
"getContinueStmt": proxyenv.GetContinueStmt,
536543
},
544+
yieldStmt: map[string]interface{}{
545+
"toString": proxyenv.ToString,
546+
"getYieldStmt": proxyenv.GetYieldStmt,
547+
},
537548
}
538549
return env
539550
}

sourcecode-parser/model/stmt.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,38 @@ func (continueStmt *ContinueStmt) hasLabel() bool {
266266
func (continueStmt *ContinueStmt) GetLabel() string {
267267
return continueStmt.Label
268268
}
269+
270+
// TODO: Implement the SwitchStmt Expr.
271+
type YieldStmt struct {
272+
JumpStmt
273+
Value *Expr
274+
}
275+
276+
type IYieldStmt interface {
277+
GetAPrimaryQlClass() string
278+
GetHalsteadID() int
279+
GetPP() string
280+
ToString() string
281+
GetValue() *Expr
282+
}
283+
284+
func (yieldStmt *YieldStmt) GetAPrimaryQlClass() string {
285+
return "YieldStmt"
286+
}
287+
288+
func (yieldStmt *YieldStmt) GetHalsteadID() int {
289+
// TODO: Implement Halstead ID calculation for YieldStmt
290+
return 0
291+
}
292+
293+
func (yieldStmt *YieldStmt) GetPP() string {
294+
return fmt.Sprintf("yield %s", yieldStmt.Value.NodeString)
295+
}
296+
297+
func (yieldStmt *YieldStmt) ToString() string {
298+
return fmt.Sprintf("yield %s", yieldStmt.Value.NodeString)
299+
}
300+
301+
func (yieldStmt *YieldStmt) GetValue() *Expr {
302+
return yieldStmt.Value
303+
}

sourcecode-parser/model/stmt_test.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,133 @@ func TestContinueStmt(t *testing.T) {
219219
assert.Equal(t, "", continueStmt.GetLabel())
220220
})
221221
}
222+
223+
func TestYieldStmt(t *testing.T) {
224+
t.Run("ToString with non-empty value", func(t *testing.T) {
225+
yieldStmt := &YieldStmt{
226+
Value: &Expr{NodeString: "42"},
227+
}
228+
assert.Equal(t, "yield 42", yieldStmt.ToString())
229+
})
230+
231+
t.Run("ToString with empty value", func(t *testing.T) {
232+
yieldStmt := &YieldStmt{
233+
Value: &Expr{NodeString: ""},
234+
}
235+
assert.Equal(t, "yield ", yieldStmt.ToString())
236+
})
237+
238+
t.Run("ToString with complex expression", func(t *testing.T) {
239+
yieldStmt := &YieldStmt{
240+
Value: &Expr{NodeString: "a + b * c"},
241+
}
242+
assert.Equal(t, "yield a + b * c", yieldStmt.ToString())
243+
})
244+
245+
t.Run("ToString with string literal", func(t *testing.T) {
246+
yieldStmt := &YieldStmt{
247+
Value: &Expr{NodeString: "\"hello world\""},
248+
}
249+
assert.Equal(t, "yield \"hello world\"", yieldStmt.ToString())
250+
})
251+
}
252+
253+
func TestYieldStmt_GetValue(t *testing.T) {
254+
t.Run("GetValue with non-nil value", func(t *testing.T) {
255+
expr := &Expr{NodeString: "42"}
256+
yieldStmt := &YieldStmt{
257+
Value: expr,
258+
}
259+
assert.Equal(t, expr, yieldStmt.GetValue())
260+
})
261+
262+
t.Run("GetValue with nil value", func(t *testing.T) {
263+
yieldStmt := &YieldStmt{
264+
Value: nil,
265+
}
266+
assert.Nil(t, yieldStmt.GetValue())
267+
})
268+
269+
t.Run("GetValue with complex expression", func(t *testing.T) {
270+
expr := &Expr{NodeString: "foo() + bar(x, y)"}
271+
yieldStmt := &YieldStmt{
272+
Value: expr,
273+
}
274+
assert.Equal(t, expr, yieldStmt.GetValue())
275+
})
276+
277+
t.Run("GetValue preserves expression reference", func(t *testing.T) {
278+
expr := &Expr{NodeString: "someValue"}
279+
yieldStmt := &YieldStmt{
280+
Value: expr,
281+
}
282+
retrievedExpr := yieldStmt.GetValue()
283+
expr.NodeString = "modifiedValue"
284+
assert.Equal(t, "modifiedValue", retrievedExpr.NodeString)
285+
})
286+
}
287+
288+
func TestYieldStmt_GetHalsteadID(t *testing.T) {
289+
t.Run("Returns zero for empty yield statement", func(t *testing.T) {
290+
yieldStmt := &YieldStmt{}
291+
assert.Equal(t, 0, yieldStmt.GetHalsteadID())
292+
})
293+
294+
t.Run("Returns zero for yield with simple value", func(t *testing.T) {
295+
yieldStmt := &YieldStmt{
296+
Value: &Expr{NodeString: "42"},
297+
}
298+
assert.Equal(t, 0, yieldStmt.GetHalsteadID())
299+
})
300+
301+
t.Run("Returns zero for yield with complex expression", func(t *testing.T) {
302+
yieldStmt := &YieldStmt{
303+
Value: &Expr{NodeString: "a + b * c"},
304+
}
305+
assert.Equal(t, 0, yieldStmt.GetHalsteadID())
306+
})
307+
308+
t.Run("Returns zero for yield with method call", func(t *testing.T) {
309+
yieldStmt := &YieldStmt{
310+
Value: &Expr{NodeString: "calculateValue()"},
311+
}
312+
assert.Equal(t, 0, yieldStmt.GetHalsteadID())
313+
})
314+
}
315+
316+
func TestYieldStmt_GetPP(t *testing.T) {
317+
t.Run("GetPP with numeric value", func(t *testing.T) {
318+
yieldStmt := &YieldStmt{
319+
Value: &Expr{NodeString: "42"},
320+
}
321+
assert.Equal(t, "yield 42", yieldStmt.GetPP())
322+
})
323+
324+
t.Run("GetPP with method call", func(t *testing.T) {
325+
yieldStmt := &YieldStmt{
326+
Value: &Expr{NodeString: "getValue()"},
327+
}
328+
assert.Equal(t, "yield getValue()", yieldStmt.GetPP())
329+
})
330+
331+
t.Run("GetPP with complex expression", func(t *testing.T) {
332+
yieldStmt := &YieldStmt{
333+
Value: &Expr{NodeString: "x + y * (z - 1)"},
334+
}
335+
assert.Equal(t, "yield x + y * (z - 1)", yieldStmt.GetPP())
336+
})
337+
338+
t.Run("GetPP with empty expression", func(t *testing.T) {
339+
yieldStmt := &YieldStmt{
340+
Value: &Expr{NodeString: ""},
341+
}
342+
assert.Equal(t, "yield ", yieldStmt.GetPP())
343+
})
344+
345+
t.Run("GetPP with string literal", func(t *testing.T) {
346+
yieldStmt := &YieldStmt{
347+
Value: &Expr{NodeString: "\"test string\""},
348+
}
349+
assert.Equal(t, "yield \"test string\"", yieldStmt.GetPP())
350+
})
351+
}

test-src/android/app/src/main/java/com/ivb/udacity/movieDetailActivity.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,18 @@ public boolean onOptionsItemSelected(MenuItem item) {
7979
i++;
8080
}
8181

82+
String message = switch (number) {
83+
case ONE -> {
84+
yield "Got a 1";
85+
}
86+
case TWO -> {
87+
yield "Got a 2";
88+
}
89+
default -> {
90+
yield a+b;
91+
}
92+
};
93+
8294
do {
8395
i++;
8496
} while (i < 10);

0 commit comments

Comments
 (0)