Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: 🍺 Support for YieldStmt statement #176

Merged
merged 3 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion sourcecode-parser/graph/construct.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ type Node struct {
ForStmt *model.ForStmt
BreakStmt *model.BreakStmt
ContinueStmt *model.ContinueStmt
} //
YieldStmt *model.YieldStmt
}

type Edge struct {
From *Node
Expand Down Expand Up @@ -180,6 +181,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 "yield_statement":
yieldNode := javalang.ParseYieldStatement(node, sourceCode)
uniqueyieldID := fmt.Sprintf("yield_%d_%d_%s", node.StartPoint().Row+1, node.StartPoint().Column+1, file)
yieldStmtNode := &Node{
ID: GenerateSha256(uniqueyieldID),
Type: "YieldStmt",
LineNumber: node.StartPoint().Row + 1,
Name: "YieldStmt",
IsExternal: true,
CodeSnippet: node.Content(sourceCode),
File: file,
isJavaSourceFile: isJavaSourceFile,
YieldStmt: yieldNode,
}
graph.AddNode(yieldStmtNode)
case "break_statement":
breakNode := javalang.ParseBreakStatement(node, sourceCode)
uniquebreakstmtID := fmt.Sprintf("breakstmt_%d_%d_%s", node.StartPoint().Row+1, node.StartPoint().Column+1, file)
Expand Down
19 changes: 16 additions & 3 deletions sourcecode-parser/graph/construct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,19 @@ func TestBuildGraphFromAST(t *testing.T) {
System.out.println(i);
break;
}
switch (day) {
case "MONDAY" -> 1;
case "TUESDAY" -> 2;
case "WEDNESDAY" -> 3;
case "THURSDAY" -> 4;
case "FRIDAY" -> 5;
case "SATURDAY" -> 6;
case "SUNDAY" -> 7;
default -> {
System.out.println("Invalid day: " + day);
yield 9; // Using 'yield' to return a value from this case
}
};
do {
System.out.println("Hello, World!");
} while (a > 0);
Expand All @@ -778,9 +791,9 @@ func TestBuildGraphFromAST(t *testing.T) {
}
}
`,
expectedNodes: 69,
expectedEdges: 4,
expectedTypes: []string{"class_declaration", "method_declaration", "binary_expression", "comp_expression", "and_expression", "or_expression", "IfStmt", "ForStmt", "WhileStmt", "DoStmt", "BreakStmt", "ContinueStmt"},
expectedNodes: 73,
expectedEdges: 5,
expectedTypes: []string{"class_declaration", "method_declaration", "binary_expression", "comp_expression", "and_expression", "or_expression", "IfStmt", "ForStmt", "WhileStmt", "DoStmt", "BreakStmt", "ContinueStmt", "YieldStmt"},
unexpectedTypes: []string{""},
},
{
Expand Down
7 changes: 7 additions & 0 deletions sourcecode-parser/graph/java/parse_statement.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,10 @@ func ParseContinueStatement(node *sitter.Node, sourcecode []byte) *model.Continu
}
return continueStmt
}

func ParseYieldStatement(node *sitter.Node, sourcecode []byte) *model.YieldStmt {
yieldStmt := &model.YieldStmt{}
yieldStmtExpr := &model.Expr{NodeString: node.Child(1).Content(sourcecode)}
yieldStmt.Value = yieldStmtExpr
return yieldStmt
}
56 changes: 56 additions & 0 deletions sourcecode-parser/graph/java/parse_statement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,59 @@ func TestParseContinueStatement(t *testing.T) {
})
}
}

func TestParseYieldStatement(t *testing.T) {
tests := []struct {
name string
input string
expected *model.YieldStmt
}{
{
name: "Simple yield statement with literal",
input: "yield 42;",
expected: &model.YieldStmt{
Value: &model.Expr{NodeString: "42"},
},
},
{
name: "Yield statement with variable",
input: "yield result;",
expected: &model.YieldStmt{
Value: &model.Expr{NodeString: "result"},
},
},
{
name: "Yield statement with expression",
input: "yield a + b;",
expected: &model.YieldStmt{
Value: &model.Expr{NodeString: "a + b"},
},
},
{
name: "Yield statement with method call",
input: "yield getValue();",
expected: &model.YieldStmt{
Value: &model.Expr{NodeString: "getValue()"},
},
},
{
name: "Yield statement with string literal",
input: "yield \"hello\";",
expected: &model.YieldStmt{
Value: &model.Expr{NodeString: "\"hello\""},
},
},
}

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 := ParseYieldStatement(node, []byte(tt.input))
assert.Equal(t, tt.expected, result)
})
}
}
11 changes: 11 additions & 0 deletions sourcecode-parser/graph/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@
return env.Node.ContinueStmt
}

func (env *Env) GetYieldStmt() *model.YieldStmt {
return env.Node.YieldStmt

Check warning on line 147 in sourcecode-parser/graph/query.go

View check run for this annotation

Codecov / codecov/patch

sourcecode-parser/graph/query.go#L146-L147

Added lines #L146 - L147 were not covered by tests
}

func QueryEntities(graph *CodeGraph, query parser.Query) (nodes [][]*Node, output [][]interface{}) {
result := make([][]*Node, 0)

Expand Down Expand Up @@ -320,6 +324,7 @@
forStmt := "ForStmt"
breakStmt := "BreakStmt"
continueStmt := "ContinueStmt"
yieldStmt := "YieldStmt"

// print query select list
for _, entity := range query.SelectList {
Expand Down Expand Up @@ -380,6 +385,8 @@
breakStmt = entity.Alias
case "ContinueStmt":
continueStmt = entity.Alias
case "YieldStmt":
yieldStmt = entity.Alias

Check warning on line 389 in sourcecode-parser/graph/query.go

View check run for this annotation

Codecov / codecov/patch

sourcecode-parser/graph/query.go#L388-L389

Added lines #L388 - L389 were not covered by tests
}
}
env := map[string]interface{}{
Expand Down Expand Up @@ -534,6 +541,10 @@
"toString": proxyenv.ToString,
"getContinueStmt": proxyenv.GetContinueStmt,
},
yieldStmt: map[string]interface{}{
"toString": proxyenv.ToString,
"getYieldStmt": proxyenv.GetYieldStmt,
},
}
return env
}
Expand Down
35 changes: 35 additions & 0 deletions sourcecode-parser/model/stmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,38 @@
func (continueStmt *ContinueStmt) GetLabel() string {
return continueStmt.Label
}

// TODO: Implement the SwitchStmt Expr.
type YieldStmt struct {
JumpStmt
Value *Expr
}

type IYieldStmt interface {
GetAPrimaryQlClass() string
GetHalsteadID() int
GetPP() string
ToString() string
GetValue() *Expr
}

func (yieldStmt *YieldStmt) GetAPrimaryQlClass() string {
return "YieldStmt"

Check warning on line 285 in sourcecode-parser/model/stmt.go

View check run for this annotation

Codecov / codecov/patch

sourcecode-parser/model/stmt.go#L284-L285

Added lines #L284 - L285 were not covered by tests
}

func (yieldStmt *YieldStmt) GetHalsteadID() int {
// TODO: Implement Halstead ID calculation for YieldStmt
return 0
}

func (yieldStmt *YieldStmt) GetPP() string {
return fmt.Sprintf("yield %s", yieldStmt.Value.NodeString)
}

func (yieldStmt *YieldStmt) ToString() string {
return fmt.Sprintf("yield %s", yieldStmt.Value.NodeString)
}

func (yieldStmt *YieldStmt) GetValue() *Expr {
return yieldStmt.Value
}
130 changes: 130 additions & 0 deletions sourcecode-parser/model/stmt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,133 @@ func TestContinueStmt(t *testing.T) {
assert.Equal(t, "", continueStmt.GetLabel())
})
}

func TestYieldStmt(t *testing.T) {
t.Run("ToString with non-empty value", func(t *testing.T) {
yieldStmt := &YieldStmt{
Value: &Expr{NodeString: "42"},
}
assert.Equal(t, "yield 42", yieldStmt.ToString())
})

t.Run("ToString with empty value", func(t *testing.T) {
yieldStmt := &YieldStmt{
Value: &Expr{NodeString: ""},
}
assert.Equal(t, "yield ", yieldStmt.ToString())
})

t.Run("ToString with complex expression", func(t *testing.T) {
yieldStmt := &YieldStmt{
Value: &Expr{NodeString: "a + b * c"},
}
assert.Equal(t, "yield a + b * c", yieldStmt.ToString())
})

t.Run("ToString with string literal", func(t *testing.T) {
yieldStmt := &YieldStmt{
Value: &Expr{NodeString: "\"hello world\""},
}
assert.Equal(t, "yield \"hello world\"", yieldStmt.ToString())
})
}

func TestYieldStmt_GetValue(t *testing.T) {
t.Run("GetValue with non-nil value", func(t *testing.T) {
expr := &Expr{NodeString: "42"}
yieldStmt := &YieldStmt{
Value: expr,
}
assert.Equal(t, expr, yieldStmt.GetValue())
})

t.Run("GetValue with nil value", func(t *testing.T) {
yieldStmt := &YieldStmt{
Value: nil,
}
assert.Nil(t, yieldStmt.GetValue())
})

t.Run("GetValue with complex expression", func(t *testing.T) {
expr := &Expr{NodeString: "foo() + bar(x, y)"}
yieldStmt := &YieldStmt{
Value: expr,
}
assert.Equal(t, expr, yieldStmt.GetValue())
})

t.Run("GetValue preserves expression reference", func(t *testing.T) {
expr := &Expr{NodeString: "someValue"}
yieldStmt := &YieldStmt{
Value: expr,
}
retrievedExpr := yieldStmt.GetValue()
expr.NodeString = "modifiedValue"
assert.Equal(t, "modifiedValue", retrievedExpr.NodeString)
})
}

func TestYieldStmt_GetHalsteadID(t *testing.T) {
t.Run("Returns zero for empty yield statement", func(t *testing.T) {
yieldStmt := &YieldStmt{}
assert.Equal(t, 0, yieldStmt.GetHalsteadID())
})

t.Run("Returns zero for yield with simple value", func(t *testing.T) {
yieldStmt := &YieldStmt{
Value: &Expr{NodeString: "42"},
}
assert.Equal(t, 0, yieldStmt.GetHalsteadID())
})

t.Run("Returns zero for yield with complex expression", func(t *testing.T) {
yieldStmt := &YieldStmt{
Value: &Expr{NodeString: "a + b * c"},
}
assert.Equal(t, 0, yieldStmt.GetHalsteadID())
})

t.Run("Returns zero for yield with method call", func(t *testing.T) {
yieldStmt := &YieldStmt{
Value: &Expr{NodeString: "calculateValue()"},
}
assert.Equal(t, 0, yieldStmt.GetHalsteadID())
})
}

func TestYieldStmt_GetPP(t *testing.T) {
t.Run("GetPP with numeric value", func(t *testing.T) {
yieldStmt := &YieldStmt{
Value: &Expr{NodeString: "42"},
}
assert.Equal(t, "yield 42", yieldStmt.GetPP())
})

t.Run("GetPP with method call", func(t *testing.T) {
yieldStmt := &YieldStmt{
Value: &Expr{NodeString: "getValue()"},
}
assert.Equal(t, "yield getValue()", yieldStmt.GetPP())
})

t.Run("GetPP with complex expression", func(t *testing.T) {
yieldStmt := &YieldStmt{
Value: &Expr{NodeString: "x + y * (z - 1)"},
}
assert.Equal(t, "yield x + y * (z - 1)", yieldStmt.GetPP())
})

t.Run("GetPP with empty expression", func(t *testing.T) {
yieldStmt := &YieldStmt{
Value: &Expr{NodeString: ""},
}
assert.Equal(t, "yield ", yieldStmt.GetPP())
})

t.Run("GetPP with string literal", func(t *testing.T) {
yieldStmt := &YieldStmt{
Value: &Expr{NodeString: "\"test string\""},
}
assert.Equal(t, "yield \"test string\"", yieldStmt.GetPP())
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,18 @@ public boolean onOptionsItemSelected(MenuItem item) {
i++;
}

String message = switch (number) {
case ONE -> {
yield "Got a 1";
}
case TWO -> {
yield "Got a 2";
}
default -> {
yield a+b;
}
};

do {
i++;
} while (i < 10);
Expand Down
Loading