Skip to content

Commit

Permalink
Merge pull request #1531 from xushiwei/typesutil
Browse files Browse the repository at this point in the history
typesutil.ExprString
  • Loading branch information
xushiwei committed Nov 8, 2023
2 parents 6e9352d + 6d28687 commit 563ada4
Show file tree
Hide file tree
Showing 5 changed files with 356 additions and 1 deletion.
46 changes: 46 additions & 0 deletions x/typesutil/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2023 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 typesutil

import (
"go/types"

"github.com/goplus/gop/ast"
"github.com/goplus/gop/token"
)

// A CheckConfig specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration.
type CheckConfig types.Config

// Check type-checks a package and returns the resulting package object and
// the first error if any. Additionally, if info != nil, Check populates each
// of the non-nil maps in the Info struct.
//
// The package is marked as complete if no errors occurred, otherwise it is
// incomplete. See Config.Error for controlling behavior in the presence of
// errors.
//
// The package is specified by a list of *ast.Files and corresponding
// file set, and the package path the package is identified with.
// The clean path must not be empty or dot (".").
func (conf *CheckConfig) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (ret *types.Package, err error) {
ret = types.NewPackage(path, "")
c := NewChecker((*types.Config)(conf), &Config{Types: ret, Fset: fset}, nil, info)
err = c.Files(nil, files)
return
}
2 changes: 1 addition & 1 deletion x/typesutil/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ type Config struct {
// Default is github.com/goplus/.
C2goBase string

// Mod represents a gop.mod object.
// Mod represents a gop.mod object (optional).
Mod *gopmod.Module
}

Expand Down
239 changes: 239 additions & 0 deletions x/typesutil/exprstring.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file implements printing of expressions.

package typesutil

import (
"bytes"
"fmt"

"github.com/goplus/gop/ast"
"github.com/goplus/gop/x/typesutil/internal/typeparams"
)

// ExprString returns the (possibly shortened) string representation for x.
// Shortened representations are suitable for user interfaces but may not
// necessarily follow Go syntax.
func ExprString(x ast.Expr) string {
var buf bytes.Buffer
WriteExpr(&buf, x)
return buf.String()
}

// WriteExpr writes the (possibly shortened) string representation for x to buf.
// Shortened representations are suitable for user interfaces but may not
// necessarily follow Go syntax.
func WriteExpr(buf *bytes.Buffer, x ast.Expr) {
// The AST preserves source-level parentheses so there is
// no need to introduce them here to correct for different
// operator precedences. (This assumes that the AST was
// generated by a Go parser.)

switch x := x.(type) {
default:
fmt.Fprintf(buf, "(ast: %T)", x) // nil, ast.BadExpr, ast.KeyValueExpr

case *ast.Ident:
buf.WriteString(x.Name)

case *ast.Ellipsis:
buf.WriteString("...")
if x.Elt != nil {
WriteExpr(buf, x.Elt)
}

case *ast.BasicLit:
buf.WriteString(x.Value)

case *ast.FuncLit:
buf.WriteByte('(')
WriteExpr(buf, x.Type)
buf.WriteString(" literal)") // shortened

case *ast.CompositeLit:
WriteExpr(buf, x.Type)
buf.WriteByte('{')
if len(x.Elts) > 0 {
buf.WriteString("…")
}
buf.WriteByte('}')

case *ast.ParenExpr:
buf.WriteByte('(')
WriteExpr(buf, x.X)
buf.WriteByte(')')

case *ast.SelectorExpr:
WriteExpr(buf, x.X)
buf.WriteByte('.')
buf.WriteString(x.Sel.Name)

case *ast.IndexExpr, *ast.IndexListExpr:
ix := typeparams.UnpackIndexExpr(x)
WriteExpr(buf, ix.X)
buf.WriteByte('[')
writeExprList(buf, ix.Indices)
buf.WriteByte(']')

case *ast.SliceExpr:
WriteExpr(buf, x.X)
buf.WriteByte('[')
if x.Low != nil {
WriteExpr(buf, x.Low)
}
buf.WriteByte(':')
if x.High != nil {
WriteExpr(buf, x.High)
}
if x.Slice3 {
buf.WriteByte(':')
if x.Max != nil {
WriteExpr(buf, x.Max)
}
}
buf.WriteByte(']')

case *ast.TypeAssertExpr:
WriteExpr(buf, x.X)
buf.WriteString(".(")
WriteExpr(buf, x.Type)
buf.WriteByte(')')

case *ast.CallExpr:
WriteExpr(buf, x.Fun)
buf.WriteByte('(')
writeExprList(buf, x.Args)
if x.Ellipsis.IsValid() {
buf.WriteString("...")
}
buf.WriteByte(')')

case *ast.StarExpr:
buf.WriteByte('*')
WriteExpr(buf, x.X)

case *ast.UnaryExpr:
buf.WriteString(x.Op.String())
WriteExpr(buf, x.X)

case *ast.BinaryExpr:
WriteExpr(buf, x.X)
buf.WriteByte(' ')
buf.WriteString(x.Op.String())
buf.WriteByte(' ')
WriteExpr(buf, x.Y)

case *ast.ArrayType:
buf.WriteByte('[')
if x.Len != nil {
WriteExpr(buf, x.Len)
}
buf.WriteByte(']')
WriteExpr(buf, x.Elt)

case *ast.StructType:
buf.WriteString("struct{")
writeFieldList(buf, x.Fields.List, "; ", false)
buf.WriteByte('}')

case *ast.FuncType:
buf.WriteString("func")
writeSigExpr(buf, x)

case *ast.InterfaceType:
buf.WriteString("interface{")
writeFieldList(buf, x.Methods.List, "; ", true)
buf.WriteByte('}')

case *ast.MapType:
buf.WriteString("map[")
WriteExpr(buf, x.Key)
buf.WriteByte(']')
WriteExpr(buf, x.Value)

case *ast.ChanType:
var s string
switch x.Dir {
case ast.SEND:
s = "chan<- "
case ast.RECV:
s = "<-chan "
default:
s = "chan "
}
buf.WriteString(s)
WriteExpr(buf, x.Value)
}
}

func writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) {
buf.WriteByte('(')
writeFieldList(buf, sig.Params.List, ", ", false)
buf.WriteByte(')')

res := sig.Results
n := res.NumFields()
if n == 0 {
// no result
return
}

buf.WriteByte(' ')
if n == 1 && len(res.List[0].Names) == 0 {
// single unnamed result
WriteExpr(buf, res.List[0].Type)
return
}

// multiple or named result(s)
buf.WriteByte('(')
writeFieldList(buf, res.List, ", ", false)
buf.WriteByte(')')
}

func writeFieldList(buf *bytes.Buffer, list []*ast.Field, sep string, iface bool) {
for i, f := range list {
if i > 0 {
buf.WriteString(sep)
}

// field list names
writeIdentList(buf, f.Names)

// types of interface methods consist of signatures only
if sig, _ := f.Type.(*ast.FuncType); sig != nil && iface {
writeSigExpr(buf, sig)
continue
}

// named fields are separated with a blank from the field type
if len(f.Names) > 0 {
buf.WriteByte(' ')
}

WriteExpr(buf, f.Type)

// ignore tag
}
}

func writeIdentList(buf *bytes.Buffer, list []*ast.Ident) {
for i, x := range list {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(x.Name)
}
}

func writeExprList(buf *bytes.Buffer, list []ast.Expr) {
for i, x := range list {
if i > 0 {
buf.WriteString(", ")
}
WriteExpr(buf, x)
}
}
16 changes: 16 additions & 0 deletions x/typesutil/imp.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package typesutil

import (
"go/types"
"syscall"

"github.com/goplus/gop"
"github.com/goplus/gop/token"
Expand All @@ -26,6 +27,16 @@ import (
"github.com/goplus/mod/gopmod"
)

// -----------------------------------------------------------------------------

type nilImporter struct{}

func (p nilImporter) Import(path string) (ret *types.Package, err error) {
return nil, syscall.ENOENT
}

// -----------------------------------------------------------------------------

type importer struct {
imp types.Importer
alt *gop.Importer
Expand All @@ -36,6 +47,9 @@ type importer struct {
}

func newImporter(imp types.Importer, mod *gopmod.Module, gop *env.Gop, fset *token.FileSet) types.Importer {
if imp == nil {
imp = nilImporter{}
}
if gop == nil {
gop = gopenv.Get()
}
Expand All @@ -52,3 +66,5 @@ func (p *importer) Import(path string) (ret *types.Package, err error) {
}
return p.alt.Import(path)
}

// -----------------------------------------------------------------------------
54 changes: 54 additions & 0 deletions x/typesutil/internal/typeparams/typeparams.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package typeparams

import (
"github.com/goplus/gop/ast"
"github.com/goplus/gop/token"
)

func PackIndexExpr(x ast.Expr, lbrack token.Pos, exprs []ast.Expr, rbrack token.Pos) ast.Expr {
switch len(exprs) {
case 0:
panic("internal error: PackIndexExpr with empty expr slice")
case 1:
return &ast.IndexExpr{
X: x,
Lbrack: lbrack,
Index: exprs[0],
Rbrack: rbrack,
}
default:
return &ast.IndexListExpr{
X: x,
Lbrack: lbrack,
Indices: exprs,
Rbrack: rbrack,
}
}
}

// IndexExpr wraps an ast.IndexExpr or ast.IndexListExpr.
//
// Orig holds the original ast.Expr from which this IndexExpr was derived.
type IndexExpr struct {
Orig ast.Expr // the wrapped expr, which may be distinct from the IndexListExpr below.
*ast.IndexListExpr
}

func UnpackIndexExpr(n ast.Node) *IndexExpr {
switch e := n.(type) {
case *ast.IndexExpr:
return &IndexExpr{e, &ast.IndexListExpr{
X: e.X,
Lbrack: e.Lbrack,
Indices: []ast.Expr{e.Index},
Rbrack: e.Rbrack,
}}
case *ast.IndexListExpr:
return &IndexExpr{e, e}
}
return nil
}

0 comments on commit 563ada4

Please sign in to comment.