Skip to content

Commit

Permalink
add declaration normalization
Browse files Browse the repository at this point in the history
  • Loading branch information
tehsphinx committed Nov 12, 2022
1 parent affc8bb commit fe72ce8
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 9 deletions.
22 changes: 13 additions & 9 deletions representation/normalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,35 @@ func (s *Representation) Normalize() error {
return errors.New("no Go package found")
}

pkg = s.normalize(pkg)
s.represent = pkg
return nil
}

func (s *Representation) normalize(pkg *ast.Package) *ast.Package {
f := ast.MergePackageFiles(pkg, ast.FilterImportDuplicates+ast.FilterUnassociatedComments)
pkg.Files = map[string]*ast.File{defaultFileName: f}

// register additional normalizations here
s.normalizeDeclarations(pkg)

s.sortInFile(f)
s.normalizeNaming(pkg)

s.represent = pkg
return nil
}

func (s *Representation) normalizeNaming(pkg *ast.Package) {
astutil.Apply(pkg, func(cursor *astutil.Cursor) bool {
node := cursor.Node()
if node == nil {
return true
}

s.collectImport(node)
s.rename(node, cursor)
s.rename(cursor)
return true
}, nil)
return pkg
}

func (s *Representation) rename(node ast.Node, cursor *astutil.Cursor) {
func (s *Representation) rename(cursor *astutil.Cursor) {
node := cursor.Node()

switch n := node.(type) {
default:
return
Expand Down
168 changes: 168 additions & 0 deletions representation/normalize_decl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package representation

import (
"go/ast"
"go/token"
"strconv"
"strings"

"golang.org/x/tools/go/ast/astutil"
)

// TODO: test with more cases
func (s *Representation) normalizeDeclarations(pkg *ast.Package) {
astutil.Apply(pkg, func(cursor *astutil.Cursor) bool {
node := cursor.Node()
if node == nil {
return true
}
var (
a = "abc"
b, c = 1, "cde"
)
_, _, _ = a, b, c

switch n := node.(type) {
default:
return true
case *ast.DeclStmt:
genDecl, ok := n.Decl.(*ast.GenDecl)
if !ok {
return true
}
if genDecl.Tok.String() != "var" {
return true
}

var keep bool
for _, spec := range genDecl.Specs {
specNode, ok := spec.(*ast.ValueSpec)
if !ok {
keep = true
continue
}

for i, name := range specNode.Names {
val := getValue(specNode.Values, i)
if val == nil {
val = getDefaultLiteralForExpression(specNode.Type)
}
val = addTypeAsNeeded(val, specNode.Type)
newDecl := newAssignment(name.Name, val)
cursor.InsertBefore(newDecl)
}
}
if !keep {
cursor.Delete()
}
}

return true
}, nil)
}

func addTypeAsNeeded(val ast.Expr, typeExpr ast.Expr) ast.Expr {
if _, ok := val.(*ast.BasicLit); !ok {
return val
}
typeIdent, ok := typeExpr.(*ast.Ident)
if !ok {
return val
}
if !needsType(typeIdent.Name) {
return val
}

return &ast.CallExpr{
Fun: typeIdent,
Args: []ast.Expr{val},
}
}

func needsType(ident string) bool {
switch ident {
case "string", "int", "float64", "complex128", "bool":
return false
}
return true
}

func newAssignment(name string, value ast.Expr) ast.Node {
return &ast.AssignStmt{
Lhs: []ast.Expr{&ast.Ident{Name: name}},
Tok: token.DEFINE,
Rhs: []ast.Expr{value},
}
}

func getValue(values []ast.Expr, i int) ast.Expr {
if len(values) > i {
return values[i]
}
return nil
}

func getDefaultLiteralForExpression(typeExpr ast.Expr) ast.Expr {
typeIdent, ok := typeExpr.(*ast.Ident)
if !ok {
return &ast.BasicLit{
Kind: token.STRING,
Value: `"unimplemented case in representer: getDefaultLiteralForExpression: typeExpr is not *ast.Ident"`,
}
}

kind := typeLookup(typeIdent.Name)
switch kind {
case token.IDENT:
return &ast.CallExpr{
Fun: typeIdent,
Args: []ast.Expr{
&ast.BasicLit{
Kind: token.INT,
Value: `0`,
},
},
}
}
return &ast.BasicLit{
Kind: kind,
Value: getDefaultLiteral(kind),
}
}

// TODO: test this. Correct string names used?
func typeLookup(name string) token.Token {
switch strings.ToLower(name) {
case "int":
return token.INT
case "float":
return token.FLOAT
case "imag":
return token.IMAG
case "char":
return token.CHAR
case "string":
return token.STRING
}
return token.IDENT
}

func getDefaultLiteral(kind token.Token) string {
switch kind {
case token.INT:
return `0`
case token.FLOAT:
return `0.0`
case token.IMAG:
return `0i`
case token.CHAR:
return `0`
case token.STRING:
return `""`
}
return `"unimplemented case in representer: getDefaultLiteral: unknown kind: ` + strconv.Itoa(int(kind)) + `"`
}

// *ast.AssignStmt: &{Lhs:[res] TokPos:110 Tok::= Rhs:[0x140001dc660]}
// *ast.Ident: res
// *ast.BasicLit: &{ValuePos:113 Kind:STRING Value:""}

0 comments on commit fe72ce8

Please sign in to comment.