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

Fix #49 and #36 - use go scanner for parsing lines and identifiers #50

Merged
merged 3 commits into from
Aug 30, 2018
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
10 changes: 3 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
language: go

go:
# - 1.0
# - 1.1
- 1.2
- 1.3
- 1.4
- 1.5
- 1.6
- 1.7
- 1.8
- 1.9
110 changes: 69 additions & 41 deletions parse/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"go/ast"
"go/parser"
"go/scanner"
"go/token"
"io"
"os"
Expand All @@ -30,7 +31,6 @@ var (
importKeyword = []byte("import")
openBrace = []byte("(")
closeBrace = []byte(")")
space = " "
genericPackage = "generic"
genericType = "generic.Type"
genericNumber = "generic.Number"
Expand All @@ -40,6 +40,63 @@ var unwantedLinePrefixes = [][]byte{
[]byte("//go:generate genny "),
}

func subIntoLiteral(lit, typeTemplate, specificType string) string {
var i int

i = strings.Index(lit[i:], typeTemplate) // find out where
if i <= -1 {
return lit
}
if i == 0 && len(typeTemplate) == len(lit) {
// If we're at the start of the word and it's an exact match
// of the literal and typeTemplate, don't wordify the replacement:
// subIntoLiteral("Something", "Something", "int") -> int

return strings.Replace(lit, typeTemplate, specificType, 1)
} else {
// Otherwise, wordify every replacement:
// subIntoLiteral("SomethingMap", "Something", int) -> IntMap
// subIntoLiteral("MySomething", "Something", int) -> MyInt
cap := wordify(specificType, unicode.IsUpper(rune(lit[0])))
return strings.Replace(lit, typeTemplate, cap, -1)
}
}

func subTypeIntoComment(line, typeTemplate, specificType string) string {
var subbed string
for _, w := range strings.Fields(line) {
subbed = subbed + subIntoLiteral(w, typeTemplate, specificType) + " "
}
return subbed
}

// Does the heavy lifting of taking a line of our code and
// sbustituting a type into there for our generic type
func subTypeIntoLine(line, typeTemplate, specificType string) string {
src := []byte(line)
var s scanner.Scanner
fset := token.NewFileSet()
file := fset.AddFile("", fset.Base(), len(src))
s.Init(file, src, nil, scanner.ScanComments)
output := ""
for {
_, tok, lit := s.Scan()
if tok == token.EOF {
break
} else if tok == token.COMMENT {
subbed := subTypeIntoComment(lit, typeTemplate, specificType)
output = output + subbed + " "
} else if tok.IsLiteral() {
subbed := subIntoLiteral(lit, typeTemplate, specificType)
output = output + subbed + " "
} else {
output = output + tok.String() + " "
}
}
return output
}

// typeSet looks like "KeyType: int, ValueType: string"
func generateSpecific(filename string, in io.ReadSeeker, typeSet map[string]string) ([]byte, error) {

// ensure we are at the beginning of the file
Expand Down Expand Up @@ -76,7 +133,6 @@ func generateSpecific(filename string, in io.ReadSeeker, typeSet map[string]stri
}
}

// go back to the start of the file
in.Seek(0, os.SEEK_SET)

var buf bytes.Buffer
Expand All @@ -85,64 +141,36 @@ func generateSpecific(filename string, in io.ReadSeeker, typeSet map[string]stri
scanner := bufio.NewScanner(in)
for scanner.Scan() {

l := scanner.Text()
line := scanner.Text()

// does this line contain generic.Type?
if strings.Contains(l, genericType) || strings.Contains(l, genericNumber) {
if strings.Contains(line, genericType) || strings.Contains(line, genericNumber) {
comment = ""
continue
}

for t, specificType := range typeSet {

// does the line contain our type
if strings.Contains(l, t) {

var newLine string
// check each word
for _, word := range strings.Fields(l) {

i := 0
for {
i = strings.Index(word[i:], t) // find out where

if i > -1 {

// if this isn't an exact match
if i > 0 && isAlphaNumeric(rune(word[i-1])) || i < len(word)-len(t) && isAlphaNumeric(rune(word[i+len(t)])) {
// replace the word with a capitolized version
word = strings.Replace(word, t, wordify(specificType, unicode.IsUpper(rune(strings.TrimLeft(word, "*&")[0]))), 1)
} else {
// replace the word as is
word = strings.Replace(word, t, specificType, 1)
}

} else {
newLine = newLine + word + space
break
}

}
}
l = newLine
if strings.Contains(line, t) {
newLine := subTypeIntoLine(line, t, specificType)
line = newLine
}
}

if comment != "" {
buf.WriteString(line(comment))
buf.WriteString(makeLine(comment))
comment = ""
}

// is this line a comment?
// TODO: should we handle /* */ comments?
if strings.HasPrefix(l, "//") {
if strings.HasPrefix(line, "//") {
// record this line to print later
comment = l
comment = line
continue
}

// write the line
buf.WriteString(line(l))
buf.WriteString(makeLine(line))
}

// write it out
Expand Down Expand Up @@ -208,7 +236,7 @@ func Generics(filename, pkgName string, in io.ReadSeeker, typeSets []map[string]
continue
}

cleanOutputLines = append(cleanOutputLines, line(scanner.Text()))
cleanOutputLines = append(cleanOutputLines, makeLine(scanner.Text()))
}

cleanOutput := strings.Join(cleanOutputLines, "")
Expand All @@ -229,7 +257,7 @@ func Generics(filename, pkgName string, in io.ReadSeeker, typeSets []map[string]
return output, nil
}

func line(s string) string {
func makeLine(s string) string {
return fmt.Sprintln(strings.TrimRight(s, linefeed))
}

Expand Down
6 changes: 6 additions & 0 deletions parse/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ var tests = []struct {
types: []map[string]string{{"Node": "int"}},
expectedOut: `test/bugreports/int_digraph.go`,
},
{
filename: "generic_new_and_make_slice.go",
in: `test/bugreports/generic_new_and_make_slice.go`,
types: []map[string]string{{"NumberType": "int"}},
expectedOut: `test/bugreports/int_new_and_make_slice.go`,
},
}

func TestParse(t *testing.T) {
Expand Down
16 changes: 16 additions & 0 deletions parse/test/bugreports/generic_new_and_make_slice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package bugreports
pdrum marked this conversation as resolved.
Show resolved Hide resolved

import "github.com/cheekybits/genny/generic"

// Tests for issues raised in github issues #36 and #49
type NumberType generic.Number

type ObjNumberType struct {
v NumberType
}

func NewNumberTypes() (*ObjNumberType, []ObjNumberType) {
n := new(ObjNumberType)
m := make([]ObjNumberType, 0)
return n, m
}
15 changes: 15 additions & 0 deletions parse/test/bugreports/int_new_and_make_slice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny

package bugreports

type ObjInt struct {
v int
}

func NewInts() (*ObjInt, []ObjInt) {
n := new(ObjInt)
m := make([]ObjInt, 0)
return n, m
}