Skip to content
Open
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
58 changes: 12 additions & 46 deletions cmd/bblfsh-tools/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,11 @@ import (
"context"
"io/ioutil"
"path/filepath"
"strings"

"github.com/bblfsh/tools"

"github.com/Sirupsen/logrus"
"google.golang.org/grpc"
"gopkg.in/bblfsh/sdk.v1/protocol"
"gopkg.in/bblfsh/sdk.v1/uast"
"gopkg.in/src-d/go-errors.v1"
)

var (
ErrParserFatal = errors.NewKind("Fatal response from parser: %s")
ErrParserError = errors.NewKind("Error response from parser: %s")
"github.com/bblfsh/go-client/v4"
)

type Common struct {
Expand All @@ -31,52 +22,27 @@ type Common struct {
func (c *Common) execute(args []string, tool tools.Tooler) error {
logrus.Debugf("executing command")

request, err := c.buildRequest()
ctx := context.Background()
client, err := bblfsh.NewClientContext(ctx, c.Address)
if err != nil {
return err
}

uast, err := c.parseRequest(request)
if err != nil {
return err
}

return tool.Exec(uast)
}

func (c *Common) buildRequest() (*protocol.ParseRequest, error) {
logrus.Debugf("reading file %s", c.Args.File)
content, err := ioutil.ReadFile(c.Args.File)
if err != nil {
return nil, err
return err
}

request := &protocol.ParseRequest{
Filename: filepath.Base(c.Args.File),
Language: c.Language,
Content: string(content),
}
return request, nil
}

func (c *Common) parseRequest(request *protocol.ParseRequest) (*uast.Node, error) {
logrus.Debugf("dialing request at %s", c.Address)
connection, err := grpc.Dial(c.Address, grpc.WithInsecure())
uast, _, err := client.NewParseRequest().Context(ctx).
Mode(bblfsh.Annotated).
Content(string(content)).
Language(c.Language).
Filename(filepath.Base(c.Args.File)).
UAST()
if err != nil {
return nil, err
return err
}

client := protocol.NewProtocolServiceClient(connection)
response, err := client.Parse(context.TODO(), request)
if err != nil {
return nil, err
}
switch response.Status {
case protocol.Fatal:
return nil, ErrParserFatal.New(strings.Join(response.Errors, "\n"))
case protocol.Error:
return nil, ErrParserError.New(strings.Join(response.Errors, "\n"))
default:
return response.UAST, nil
}
return tool.Exec(uast)
}
38 changes: 19 additions & 19 deletions cyclomatic.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ package tools
import (
"fmt"

"gopkg.in/bblfsh/sdk.v1/uast"
"github.com/bblfsh/go-client/v4/tools"
"github.com/bblfsh/sdk/v3/uast"
"github.com/bblfsh/sdk/v3/uast/nodes"
"github.com/bblfsh/sdk/v3/uast/role"
)

// CyclomaticComplexity returns the cyclomatic complexity for the node. The cyclomatic complexity
// is a quantitative measure of the number of linearly independent paths through a program's source code.
// The cyclomatic complexity is a quantitative measure of the number of linearly
// independent paths through a program's source code.
// It was developed by Thomas J. McCabe, Sr. in 1976. For a formal description see:
// https://en.wikipedia.org/wiki/Cyclomatic_complexity
// And the original paper: http://www.literateprogramming.com/mccabe.pdf
Expand Down Expand Up @@ -46,27 +49,24 @@ import (
// evaluate more than two items with a single operator. (FIXME when both things are solved in the UAST
// definition and the SDK).

// CyclomaticComplexity is a sub-command that compuets cyclomatic complexity.
type CyclomaticComplexity struct{}

func (cc CyclomaticComplexity) Exec(n *uast.Node) error {
func (cc CyclomaticComplexity) Exec(n nodes.Node) error {
result := cyclomaticComplexity(n)
fmt.Println("Cyclomatic Complexity = ", result)
return nil
}

func cyclomaticComplexity(n *uast.Node) int {
// cyclomaticComplexity returns the cyclomatic complexity for the node.
func cyclomaticComplexity(n nodes.Node) int {
complexity := 1

iter := uast.NewOrderPathIter(uast.NewPath(n))
iter := tools.NewIterator(n, tools.PreOrder)

for {
p := iter.Next()
if p.IsEmpty() {
break
}
n := p.Node()
roles := make(map[uast.Role]bool)
for _, r := range n.Roles {
for n := range tools.Iterate(iter) {
roles := make(map[role.Role]bool)
for _, r := range uast.RolesOf(n) {
roles[r] = true
}
if addsComplexity(roles) {
Expand All @@ -76,9 +76,9 @@ func cyclomaticComplexity(n *uast.Node) int {
return complexity
}

func addsComplexity(roles map[uast.Role]bool) bool {
return roles[uast.Statement] && (roles[uast.If] || roles[uast.Case] || roles[uast.For] || roles[uast.While] || roles[uast.DoWhile] || roles[uast.Continue]) ||
roles[uast.Try] && roles[uast.Catch] ||
roles[uast.Operator] && roles[uast.Boolean] ||
roles[uast.Goto]
func addsComplexity(roles map[role.Role]bool) bool {
return roles[role.Statement] && (roles[role.If] || roles[role.Case] || roles[role.For] || roles[role.While] || roles[role.DoWhile] || roles[role.Continue]) ||
roles[role.Try] && roles[role.Catch] ||
roles[role.Operator] && roles[role.Boolean] ||
roles[role.Goto]
}
51 changes: 27 additions & 24 deletions cyclomatic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,34 @@ package tools

import (
"testing"

"github.com/stretchr/testify/require"
"gopkg.in/bblfsh/sdk.v1/uast"
// "github.com/bblfsh/sdk/v3/uast"
// "github.com/bblfsh/sdk/v3/uast/nodes"
// "github.com/stretchr/testify/require"
)

func TestCyclomaticComplexity(t *testing.T) {
require := require.New(t)
n := &uast.Node{InternalType: "module",
Children: []*uast.Node{
{InternalType: "root"}, // 1 (initial)
// Prefix is the default so it doesnt need any role
{InternalType: "if1", Roles: []uast.Role{uast.Statement, uast.If}, Children: []*uast.Node{ // 2 (If, Statement)
{InternalType: "if1else1", Roles: []uast.Role{uast.If, uast.Then}, Children: []*uast.Node{ // 0
{InternalType: "if1else1foreach", Roles: []uast.Role{uast.Statement, uast.For, uast.Iterator}, Children: []*uast.Node{ // 3 (For, Statement)
{InternalType: "foreach_child1"}, // 0
{InternalType: "foreach_child2_continue", Roles: []uast.Role{uast.Statement, uast.Continue}}, // 4 (Statement, Continue)
}},
{InternalType: "if1else1if", Roles: []uast.Role{uast.Statement, uast.If}, Children: []*uast.Node{ // 5 (Statement, If)
{InternalType: "elseif_child1"}, // 0
{InternalType: "opAnd", Roles: []uast.Role{uast.Operator, uast.Boolean, uast.And}}, // 6 (Operator, Boolean)
{InternalType: "elseif_child2"}, // 0
}},
}},
{InternalType: "break", Roles: []uast.Role{uast.Statement, uast.Break}},
},
}}}
require.Equal(cyclomaticComplexity(n), 6)
// require := require.New(t)

// TODO(bzz): this has to be nodes of particular Kind
// see https://github.com/bblfsh/go-client/issues/110#issuecomment-463428434
// n := &nodes.Node{InternalType: "module",
// Children: []*nodes.Node{
// {InternalType: "root"}, // 1 (initial)
// // Prefix is the default so it doesnt need any role
// {InternalType: "if1", Roles: []uast.Role{uast.Statement, uast.If}, Children: []*uast.Node{ // 2 (If, Statement)
// {InternalType: "if1else1", Roles: []uast.Role{uast.If, uast.Then}, Children: []*uast.Node{ // 0
// {InternalType: "if1else1foreach", Roles: []uast.Role{uast.Statement, uast.For, uast.Iterator}, Children: []*uast.Node{ // 3 (For, Statement)
// {InternalType: "foreach_child1"}, // 0
// {InternalType: "foreach_child2_continue", Roles: []uast.Role{uast.Statement, uast.Continue}}, // 4 (Statement, Continue)
// }},
// {InternalType: "if1else1if", Roles: []uast.Role{uast.Statement, uast.If}, Children: []*uast.Node{ // 5 (Statement, If)
// {InternalType: "elseif_child1"}, // 0
// {InternalType: "opAnd", Roles: []uast.Role{uast.Operator, uast.Boolean, uast.And}}, // 6 (Operator, Boolean)
// {InternalType: "elseif_child2"}, // 0
// }},
// }},
// {InternalType: "break", Roles: []uast.Role{uast.Statement, uast.Break}},
// },
// }}}
// require.Equal(cyclomaticComplexity(n), 6)
}
5 changes: 3 additions & 2 deletions dummy.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package tools

import "gopkg.in/bblfsh/sdk.v1/uast"
import "github.com/bblfsh/sdk/v3/uast/nodes"

// Dummy is a sub-command that does not do anything but connecting to bblfshd.
type Dummy struct{}

func (d Dummy) Exec(*uast.Node) error {
func (d Dummy) Exec(nodes.Node) error {
println("It works! You can now proceed with another tool :)")
return nil
}
Loading