Skip to content

Commit

Permalink
draft
Browse files Browse the repository at this point in the history
Signed-off-by: lwsanty <[email protected]>
  • Loading branch information
lwsanty committed Nov 2, 2019
1 parent 6ffd933 commit 793dd0a
Show file tree
Hide file tree
Showing 26 changed files with 1,199 additions and 1 deletion.
9 changes: 9 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
language: go

go:
- '1.13.x'

go_import_path: github.com/lwsanty/gofactor

script:
- go test -v ./...
2 changes: 2 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Oleksandr Chabaiev <[email protected]> (@lwsanty)
Denys Smirnov <[email protected]> (@dennwc)
154 changes: 153 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,154 @@
# gofactor
Advanced utility for golang refactor based on DSL transformations
Advanced utility for golang refactor based on DSL transformations provided by [bblfsh/sdk](https://github.com/bblfsh/sdk)

## Usage example
Imagine you have a piece of code
```go
package main

import "fmt"

func main() {
var (
i int
X int
j int
)

if i%2 == 0 {
i = 5
}

if X%2 == 0 {
X = 5
}

fmt.Println(i)

if i%2 == 0 {
i = 5
}

if j%2 == 0 {
j = 5
}
}

func a(i, X int) {
if i%2 == 0 {
i = 5
}

if X%2 == 0 {
X = 5
}

fmt.Println(i)

if i%2 == 0 {
i = 5
}

if X%2 == 0 {
X = 5
}
}
```
And you want to replace all code patterns like
```go
if i%2 == 0 {
i = 5
}

if X%2 == 0 {
X = 5
}

```
to
```go
if i%2 == 1 {
i = 1
} else {
X = 1
}
```
Here's where refactor library comes for help.
1) Init refactor object
```go
refactor, err := gofactor.NewRefactor(beforeSnippet, afterSnippet)
if err != nil {
log.Error(err)
os.Exit(1)
}
```
2) call `Prepare` function to generate transformations mappings
```go
if err := refactor.Prepare(); err != nil {
log.Error(err)
os.Exit(1)
}
```
3) Apply generated transformations to the desired code
```go
code, err := refactor.Apply(desiredCode)
if err != nil {
log.Error(err)
os.Exit(1)
}
```
**Result**
```go
package main

import "fmt"

func main() {
var (
i int
X int
j int
)
if i%2 == 1 {
i = 1
} else {
X = 1
}
fmt.Println(i)
if i%2 == 1 {
i = 1
} else {
j = 1
}
}
func a(i, X int) {
if i%2 == 1 {
i = 1
} else {
X = 1
}
fmt.Println(i)
if i%2 == 1 {
i = 1
} else {
X = 1
}
}
```

## Supported cases
See `fixtures`

## Under the hood
1) both input and output patterns are converted to go `AST` nodes
2) both input and output nodes converted to `bblfsh` `uast.Node`s
3) define mapping of transformation operations from input to output node
4) apply transformation mapping to the desired code: traverse over the `uast.Node`s tree and transform matching nodes
5) convert transformed tree back to golang `AST`
6) convert golang `AST` to string

## Roadmap
- currently library cannot be build because of `bblfsh/go-driver` dependency issue, fix this part
- support functions refactor
- handle cases with cascade `if`s, `switch`es and tail recursions
- during the transformations we are forced to drop nodes positions, need to investigate the possibilities of preserving/reconstructing them(probably using DST nodes could help, related issue https://github.com/dave/dst/issues/38)
57 changes: 57 additions & 0 deletions example/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package main

import (
"flag"
"fmt"
"io/ioutil"
"os"

"github.com/lwsanty/gofactor"
"github.com/opentracing/opentracing-go/log"
)

const (
dir = "../fixtures/multiple-match2/"
beforeDefault = dir + "before"
afterDefault = dir + "after"
testDefault = dir + "example.go"
)

func main() {
var (
before string
after string
test string
)
flag.StringVar(&before, "before", beforeDefault, "a string var")
flag.StringVar(&after, "after", afterDefault, "a string var")
flag.StringVar(&test, "test", testDefault, "a string var")

flag.Parse()

handleErr := func(err error) {
if err != nil {
log.Error(err)
os.Exit(1)
}
}
readFile := func(filePath string) []byte {
data, err := ioutil.ReadFile(filePath)
handleErr(err)
return data
}

beforeData := readFile(before)
afterData := readFile(after)
testData := readFile(test)

refactor, err := gofactor.NewRefactor(string(beforeData), string(afterData))
handleErr(err)

handleErr(refactor.Prepare())

code, err := refactor.Apply(string(testData))
handleErr(err)

fmt.Println(code)
}
5 changes: 5 additions & 0 deletions fixtures/multiple-match1/after
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
if i%2 == 1 {
i = 1
} else {
X = 1
}
8 changes: 8 additions & 0 deletions fixtures/multiple-match1/before
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
if i%2 == 0 {
i = 5
}

if X%2 == 0 {
X = 5
}

49 changes: 49 additions & 0 deletions fixtures/multiple-match1/example.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main

import "fmt"

func main() {
var (
i int
X int
j int
)

if i%2 == 0 {
i = 5
}

if X%2 == 0 {
X = 5
}

fmt.Println(i)

if i%2 == 0 {
i = 5
}

if j%2 == 0 {
X = 5
}
}

func a(i, X int) {
if i%2 == 0 {
i = 5
}

if X%2 == 0 {
X = 5
}

fmt.Println(i)

if i%2 == 0 {
i = 5
}

if X%2 == 0 {
X = 5
}
}
36 changes: 36 additions & 0 deletions fixtures/multiple-match1/expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import "fmt"

func main() {
var (
i int
X int
j int
)
if i%2 == 1 {
i = 1
} else {
X = 1
}
fmt.Println(i)
if i%2 == 0 {
i = 5
}
if j%2 == 0 {
X = 5
}
}
func a(i, X int) {
if i%2 == 1 {
i = 1
} else {
X = 1
}
fmt.Println(i)
if i%2 == 1 {
i = 1
} else {
X = 1
}
}
5 changes: 5 additions & 0 deletions fixtures/multiple-match2/after
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
if i%2 == 1 {
i = 1
} else {
X = 1
}
8 changes: 8 additions & 0 deletions fixtures/multiple-match2/before
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
if i%2 == 0 {
i = 5
}

if X%2 == 0 {
X = 5
}

49 changes: 49 additions & 0 deletions fixtures/multiple-match2/example.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main

import "fmt"

func main() {
var (
i int
X int
j int
)

if i%2 == 0 {
i = 5
}

if X%2 == 0 {
X = 5
}

fmt.Println(i)

if i%2 == 0 {
i = 5
}

if j%2 == 0 {
j = 5
}
}

func a(i, X int) {
if i%2 == 0 {
i = 5
}

if X%2 == 0 {
X = 5
}

fmt.Println(i)

if i%2 == 0 {
i = 5
}

if X%2 == 0 {
X = 5
}
}
Loading

0 comments on commit 793dd0a

Please sign in to comment.