Repository: goplus/gop
Pull request #1175 (2022-05-17 01:38:09)

gop/ast/fromgo


Patch:
diff --git a/ast/fromgo/gopast.go b/ast/fromgo/gopast.go
new file mode 100644
index 00000000..658ce996
--- /dev/null
+++ b/ast/fromgo/gopast.go
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package fromgo
+
+import (
+	"go/ast"
+	"go/token"
+	"log"
+	"reflect"
+
+	gopast "github.com/goplus/gop/ast"
+	goptoken "github.com/goplus/gop/token"
+)
+
+// ----------------------------------------------------------------------------
+
+func gopExpr(val ast.Expr) gopast.Expr {
+	if val == nil {
+		return nil
+	}
+	switch v := val.(type) {
+	case *ast.Ident:
+		return gopIdent(v)
+	case *ast.SelectorExpr:
+		return &gopast.SelectorExpr{
+			X:   gopExpr(v.X),
+			Sel: gopIdent(v.Sel),
+		}
+	case *ast.SliceExpr:
+		return &gopast.SliceExpr{
+			X:      gopExpr(v.X),
+			Lbrack: v.Lbrack,
+			Low:    gopExpr(v.Low),
+			High:   gopExpr(v.High),
+			Max:    gopExpr(v.Max),
+			Slice3: v.Slice3,
+			Rbrack: v.Rbrack,
+		}
+	case *ast.StarExpr:
+		return &gopast.StarExpr{
+			Star: v.Star,
+			X:    gopExpr(v.X),
+		}
+	case *ast.MapType:
+		return &gopast.MapType{
+			Map:   v.Map,
+			Key:   gopType(v.Key),
+			Value: gopType(v.Value),
+		}
+	case *ast.StructType:
+		return &gopast.StructType{
+			Struct: v.Struct,
+			Fields: gopFieldList(v.Fields),
+		}
+	case *ast.FuncType:
+		return gopFuncType(v)
+	case *ast.InterfaceType:
+		return &gopast.InterfaceType{
+			Interface: v.Interface,
+			Methods:   gopFieldList(v.Methods),
+		}
+	case *ast.ArrayType:
+		return &gopast.ArrayType{
+			Lbrack: v.Lbrack,
+			Len:    gopExpr(v.Len),
+			Elt:    gopType(v.Elt),
+		}
+	case *ast.ChanType:
+		return &gopast.ChanType{
+			Begin: v.Begin,
+			Arrow: v.Arrow,
+			Dir:   gopast.ChanDir(v.Dir),
+			Value: gopType(v.Value),
+		}
+	case *ast.BasicLit:
+		return gopBasicLit(v)
+	case *ast.BinaryExpr:
+		return &gopast.BinaryExpr{
+			X:     gopExpr(v.X),
+			OpPos: v.OpPos,
+			Op:    goptoken.Token(v.Op),
+			Y:     gopExpr(v.Y),
+		}
+	case *ast.UnaryExpr:
+		return &gopast.UnaryExpr{
+			OpPos: v.OpPos,
+			Op:    goptoken.Token(v.Op),
+			X:     gopExpr(v.X),
+		}
+	case *ast.CallExpr:
+		return &gopast.CallExpr{
+			Fun:      gopExpr(v.Fun),
+			Lparen:   v.Lparen,
+			Args:     gopExprs(v.Args),
+			Ellipsis: v.Ellipsis,
+			Rparen:   v.Rparen,
+		}
+	case *ast.IndexExpr:
+		return &gopast.IndexExpr{
+			X:      gopExpr(v.X),
+			Lbrack: v.Lbrack,
+			Index:  gopExpr(v.Index),
+			Rbrack: v.Rbrack,
+		}
+	case *ast.ParenExpr:
+		return &gopast.ParenExpr{
+			Lparen: v.Lparen,
+			X:      gopExpr(v.X),
+			Rparen: v.Rparen,
+		}
+	case *ast.CompositeLit:
+		return &gopast.CompositeLit{
+			Type:   gopType(v.Type),
+			Lbrace: v.Lbrace,
+			Elts:   gopExprs(v.Elts),
+			Rbrace: v.Rbrace,
+		}
+	case *ast.FuncLit:
+		return &gopast.FuncLit{
+			Type: gopFuncType(v.Type),
+			Body: &gopast.BlockStmt{}, // skip closure body
+		}
+	case *ast.TypeAssertExpr:
+		return &gopast.TypeAssertExpr{
+			X:      gopExpr(v.X),
+			Lparen: v.Lparen,
+			Type:   gopType(v.Type),
+			Rparen: v.Rparen,
+		}
+	case *ast.KeyValueExpr:
+		return &gopast.KeyValueExpr{
+			Key:   gopExpr(v.Key),
+			Colon: v.Colon,
+			Value: gopExpr(v.Value),
+		}
+	case *ast.Ellipsis:
+		return &gopast.Ellipsis{
+			Ellipsis: v.Ellipsis,
+			Elt:      gopExpr(v.Elt),
+		}
+	}
+	log.Panicln("goExpr: unknown expr -", reflect.TypeOf(val))
+	return nil
+}
+
+func gopExprs(vals []ast.Expr) []gopast.Expr {
+	n := len(vals)
+	if n == 0 {
+		return nil
+	}
+	ret := make([]gopast.Expr, n)
+	for i, v := range vals {
+		ret[i] = gopExpr(v)
+	}
+	return ret
+}
+
+// ----------------------------------------------------------------------------
+
+func gopFuncType(v *ast.FuncType) *gopast.FuncType {
+	return &gopast.FuncType{
+		Func:    v.Func,
+		Params:  gopFieldList(v.Params),
+		Results: gopFieldList(v.Results),
+	}
+}
+
+func gopType(v ast.Expr) gopast.Expr {
+	return gopExpr(v)
+}
+
+func gopBasicLit(v *ast.BasicLit) *gopast.BasicLit {
+	if v == nil {
+		return nil
+	}
+	return &gopast.BasicLit{
+		ValuePos: v.ValuePos,
+		Kind:     goptoken.Token(v.Kind),
+		Value:    v.Value,
+	}
+}
+
+func gopIdent(v *ast.Ident) *gopast.Ident {
+	if v == nil {
+		return nil
+	}
+	return &gopast.Ident{
+		NamePos: v.NamePos,
+		Name:    v.Name,
+	}
+}
+
+func gopIdents(names []*ast.Ident) []*gopast.Ident {
+	ret := make([]*gopast.Ident, len(names))
+	for i, v := range names {
+		ret[i] = gopIdent(v)
+	}
+	return ret
+}
+
+// ----------------------------------------------------------------------------
+
+func gopField(v *ast.Field) *gopast.Field {
+	return &gopast.Field{
+		Names: gopIdents(v.Names),
+		Type:  gopType(v.Type),
+		Tag:   gopBasicLit(v.Tag),
+	}
+}
+
+func gopFieldList(v *ast.FieldList) *gopast.FieldList {
+	if v == nil {
+		return nil
+	}
+	list := make([]*gopast.Field, len(v.List))
+	for i, item := range v.List {
+		list[i] = gopField(item)
+	}
+	return &gopast.FieldList{Opening: v.Opening, List: list, Closing: v.Closing}
+}
+
+func gopFuncDecl(v *ast.FuncDecl) *gopast.FuncDecl {
+	return &gopast.FuncDecl{
+		Recv: gopFieldList(v.Recv),
+		Name: gopIdent(v.Name),
+		Type: gopFuncType(v.Type),
+		Body: &gopast.BlockStmt{}, // ignore function body
+	}
+}
+
+// ----------------------------------------------------------------------------
+
+func gopImportSpec(spec *ast.ImportSpec) *gopast.ImportSpec {
+	return &gopast.ImportSpec{
+		Name:   gopIdent(spec.Name),
+		Path:   gopBasicLit(spec.Path),
+		EndPos: spec.EndPos,
+	}
+}
+
+func gopTypeSpec(spec *ast.TypeSpec) *gopast.TypeSpec {
+	return &gopast.TypeSpec{
+		Name:   gopIdent(spec.Name),
+		Assign: spec.Assign,
+		Type:   gopType(spec.Type),
+	}
+}
+
+func gopValueSpec(spec *ast.ValueSpec) *gopast.ValueSpec {
+	return &gopast.ValueSpec{
+		Names:  gopIdents(spec.Names),
+		Type:   gopType(spec.Type),
+		Values: gopExprs(spec.Values),
+	}
+}
+
+func gopGenDecl(v *ast.GenDecl) *gopast.GenDecl {
+	specs := make([]gopast.Spec, len(v.Specs))
+	for i, spec := range v.Specs {
+		switch v.Tok {
+		case token.IMPORT:
+			specs[i] = gopImportSpec(spec.(*ast.ImportSpec))
+		case token.TYPE:
+			specs[i] = gopTypeSpec(spec.(*ast.TypeSpec))
+		case token.VAR, token.CONST:
+			specs[i] = gopValueSpec(spec.(*ast.ValueSpec))
+		default:
+			log.Panicln("goGenDecl: unknown spec -", v.Tok)
+		}
+	}
+	return &gopast.GenDecl{
+		TokPos: v.TokPos,
+		Tok:    goptoken.Token(v.Tok),
+		Lparen: v.Lparen,
+		Specs:  specs,
+		Rparen: v.Rparen,
+	}
+}
+
+// ----------------------------------------------------------------------------
+
+func gopDecl(decl ast.Decl) gopast.Decl {
+	switch v := decl.(type) {
+	case *ast.GenDecl:
+		return gopGenDecl(v)
+	case *ast.FuncDecl:
+		return gopFuncDecl(v)
+	}
+	log.Panicln("goDecl: unkown decl -", reflect.TypeOf(decl))
+	return nil
+}
+
+func gopDecls(decls []ast.Decl) []gopast.Decl {
+	ret := make([]gopast.Decl, len(decls))
+	for i, decl := range decls {
+		ret[i] = gopDecl(decl)
+	}
+	return ret
+}
+
+// ----------------------------------------------------------------------------
+
+const (
+	KeepFuncBody = 1 << iota
+	KeepCgo
+)
+
+func ASTFile(f *ast.File, mode int) *gopast.File {
+	if (mode & KeepFuncBody) != 0 {
+		log.Panicln("ASTFile: doesn't support keeping func body now")
+	}
+	if (mode & KeepCgo) != 0 {
+		log.Panicln("ASTFile: doesn't support keeping cgo now")
+	}
+	return &gopast.File{
+		Package: f.Package,
+		Name:    gopIdent(f.Name),
+		Decls:   gopDecls(f.Decls),
+	}
+}
+
+// ----------------------------------------------------------------------------
diff --git a/ast/togo/goast.go b/ast/togo/goast.go
index f8ec8060..4dbac88a 100644
--- a/ast/togo/goast.go
+++ b/ast/togo/goast.go
@@ -1,3 +1,19 @@
+/*
+ * Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package togo
 
 import (
@@ -116,7 +132,7 @@ func goExpr(val gopast.Expr) ast.Expr {
 	case *gopast.FuncLit:
 		return &ast.FuncLit{
 			Type: goFuncType(v.Type),
-			Body: nil,
+			Body: &ast.BlockStmt{}, // skip closure body
 		}
 	case *gopast.TypeAssertExpr:
 		return &ast.TypeAssertExpr{
@@ -304,7 +320,7 @@ const (
 
 func ASTFile(f *gopast.File, mode int) *ast.File {
 	if (mode & KeepFuncBody) != 0 {
-		log.Panicln("ASTFile: doesn't support keep func body now")
+		log.Panicln("ASTFile: doesn't support keeping func body now")
 	}
 	return &ast.File{
 		Package: f.Package,


Test patch:
diff --git a/ast/fromgo/gopast_test.go b/ast/fromgo/gopast_test.go
new file mode 100644
index 00000000..4d0e0bdd
--- /dev/null
+++ b/ast/fromgo/gopast_test.go
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package fromgo
+
+import (
+	"bytes"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"testing"
+
+	"github.com/goplus/gop/format"
+)
+
+func testAST(t *testing.T, from, to string) {
+	fset := token.NewFileSet()
+	f, err := parser.ParseFile(fset, "foo.go", from, 0)
+	if err != nil {
+		t.Fatal("parser.ParseFile:", err)
+	}
+	gopf := ASTFile(f, 0)
+	var b bytes.Buffer
+	err = format.Node(&b, fset, gopf)
+	if err != nil {
+		t.Fatal("format.Node:", err)
+	}
+	result := b.String()
+	if to != result {
+		t.Fatalf("\nResult:\n%s\nExpected:\n%s\n", result, to)
+	}
+}
+
+func test(t *testing.T, src string) {
+	testAST(t, src, src)
+}
+
+func testPanic(t *testing.T, panicMsg string, doPanic func()) {
+	t.Run(panicMsg, func(t *testing.T) {
+		defer func() {
+			if e := recover(); e == nil {
+				t.Fatal("testPanic: no error?")
+			} else if msg := e.(string); msg != panicMsg {
+				t.Fatalf("\nResult:\n%s\nExpected Panic:\n%s\n", msg, panicMsg)
+			}
+		}()
+		doPanic()
+	})
+}
+
+func TestErrASTFile(t *testing.T) {
+	testPanic(t, "ASTFile: doesn't support keeping cgo now\n", func() {
+		ASTFile(nil, KeepCgo)
+	})
+	testPanic(t, "ASTFile: doesn't support keeping func body now\n", func() {
+		ASTFile(nil, KeepFuncBody)
+	})
+}
+
+func TestErrDecl(t *testing.T) {
+	testPanic(t, "goDecl: unkown decl - <nil>\n", func() {
+		gopDecl(nil)
+	})
+	testPanic(t, "goGenDecl: unknown spec - ILLEGAL\n", func() {
+		gopGenDecl(&ast.GenDecl{
+			Specs: []ast.Spec{nil},
+		})
+	})
+}
+
+func TestErrExpr(t *testing.T) {
+	testPanic(t, "goExpr: unknown expr - *ast.BadExpr\n", func() {
+		gopExpr(&ast.BadExpr{})
+	})
+}
+
+func TestBasic(t *testing.T) {
+	test(t, `package main
+
+import "fmt"
+
+type a struct {
+	v   map[int]chan int
+	arr *[2]func()
+	i   interface{}
+}
+
+var b = &a{
+	arr: &[2]func(){
+		nil,
+		func() {},
+	},
+}
+
+const c = (10 + 20) * 2
+
+var d = b.arr[1]
+
+var e = b.arr[:1]
+
+var f = a.i.(func() (int))()
+
+func foo(v ...interface{}) {}
+`)
+}