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

typesutil.ExprString #1531

Merged
merged 1 commit into from
Nov 8, 2023
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
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
}
Loading