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

os.File: support Gop_Enum #205

Merged
merged 3 commits into from
Jul 5, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
52 changes: 51 additions & 1 deletion builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -1222,6 +1222,28 @@ type builtinMethod struct {
eargs bmExargs
}

func (p *builtinMethod) Results() *types.Tuple {
return p.fn.Type().(*types.Signature).Results()
}

func (p *builtinMethod) Params() *types.Tuple {
params := p.fn.Type().(*types.Signature).Params()
n := params.Len() - len(p.eargs) - 1
if n <= 0 {
return nil
}
ret := make([]*types.Var, n)
for i := 0; i < n; i++ {
ret[i] = params.At(i + 1)
}
return types.NewTuple(ret...)
}

type mthdSignature interface {
Results() *types.Tuple
Params() *types.Tuple
}

type builtinTI struct {
typ types.Type
methods []*builtinMethod
Expand All @@ -1235,6 +1257,16 @@ func (p *builtinTI) Method(i int) *builtinMethod {
return p.methods[i]
}

func (p *builtinTI) lookupByName(name string) mthdSignature {
for i, n := 0, p.NumMethods(); i < n; i++ {
method := p.Method(i)
if method.name == name {
return method
}
}
return nil
}

var (
tyMap types.Type = types.NewMap(types.Typ[types.Invalid], types.Typ[types.Invalid])
tyChan types.Type = types.NewChan(0, types.Typ[types.Invalid])
Expand All @@ -1244,10 +1276,26 @@ var (
func initBuiltinTIs(pkg *Package) {
strconv := pkg.Import("strconv")
strings := pkg.Import("strings")
os := pkg.Import("os")
ioxTI := (*builtinTI)(nil)
ioxPkg := pkg.conf.PkgPathIox
if debugImportIox && ioxPkg == "" {
ioxPkg = "github.com/goplus/gox/internal/iox"
}
if ioxPkg != "" {
iox := pkg.Import(ioxPkg)
ioxTI = &builtinTI{
typ: os.Ref("File").Type(),
methods: []*builtinMethod{
{"Gop_Enum", iox.Ref("EnumLines"), nil},
},
}
}
btiMap := new(typeutil.Map)
btoLen := types.Universe.Lookup("len")
btoCap := types.Universe.Lookup("cap")
tis := []*builtinTI{
ioxTI,
{
typ: types.Typ[types.Float64],
methods: []*builtinMethod{
Expand Down Expand Up @@ -1345,7 +1393,9 @@ func initBuiltinTIs(pkg *Package) {
},
}
for _, ti := range tis {
btiMap.Set(ti.typ, ti)
if ti != nil {
btiMap.Set(ti.typ, ti)
}
}
pkg.cb.btiMap = btiMap
}
Expand Down
45 changes: 37 additions & 8 deletions builtin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,47 @@ var (
gblConf = getConf()
)

func init() {
debugImportIox = true
}

func getConf() *Config {
fset := token.NewFileSet()
imp := packages.NewImporter(fset)
return &Config{Fset: fset, Importer: imp}
}

func TestGetBuiltinTI(t *testing.T) {
pkg := NewPackage("", "foo", nil)
cb := &pkg.cb
if cb.getBuiltinTI(types.NewPointer(types.Typ[0])) != nil {
t.Fatal("TestGetBuiltinTI failed")
}
tiStr := cb.getBuiltinTI(types.Typ[types.String])
sig := tiStr.lookupByName("Index")
if sig == nil || sig.Params().Len() != 1 {
t.Fatal("string.Index (Params):", sig)
}
if sig == nil || sig.Results().Len() != 1 {
t.Fatal("string.Index (Results):", sig)
}
if tsig := tiStr.lookupByName("__unknown"); tsig != nil {
t.Fatal("tsig:", tsig)
}
}

func TestFindMethodType(t *testing.T) {
pkg := NewPackage("", "foo", nil)
tyFile := pkg.Import("os").Ref("File").Type().(*types.Named)
sig := findMethodType(&pkg.cb, tyFile, "Gop_Enum")
if sig == nil || sig.Params().Len() != 0 {
t.Fatal("os.File.GopEnum (Params):", sig)
}
if sig == nil || sig.Results().Len() != 1 {
t.Fatal("os.File.GopEnum (Results):", sig)
}
}

func TestContractName(t *testing.T) {
testcases := []struct {
Contract
Expand Down Expand Up @@ -241,7 +276,8 @@ func TestIsFunc(t *testing.T) {
func TestCheckUdt(t *testing.T) {
o := types.NewNamed(types.NewTypeName(token.NoPos, nil, "foo", nil), types.Typ[types.Int], nil)
var frs forRangeStmt
if _, ok := frs.checkUdt(o); ok {
var cb CodeBuilder
if _, ok := frs.checkUdt(&cb, o); ok {
t.Fatal("findMethod failed: bar exists?")
}
}
Expand Down Expand Up @@ -829,13 +865,6 @@ func TestLoadExpr(t *testing.T) {
}
}

func TestGetBuiltinTI(t *testing.T) {
var cb CodeBuilder
if cb.getBuiltinTI(types.NewPointer(types.Typ[0])) != nil {
t.Fatal("TestGetBuiltinTI failed")
}
}

func TestRef(t *testing.T) {
defer func() {
if e := recover(); e == nil {
Expand Down
7 changes: 5 additions & 2 deletions codebuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -1603,7 +1603,7 @@ func denoteRecv(v *ast.SelectorExpr) *Element {
}

func (p *CodeBuilder) method(
o methodList, name, aliasName string, flag MemberFlag, arg *Element, src ast.Node) MemberKind {
o methodList, name, aliasName string, flag MemberFlag, arg *Element, src ast.Node) (kind MemberKind) {
for i, n := 0, o.NumMethods(); i < n; i++ {
method := o.Method(i)
v := method.Name()
Expand All @@ -1625,7 +1625,10 @@ func (p *CodeBuilder) method(
return MemberMethod
}
}
return MemberInvalid
if t, ok := o.(*types.Named); ok {
kind = p.btiMethod(p.getBuiltinTI(t), name, aliasName, flag, arg, src)
}
return
}

func (p *CodeBuilder) btiMethod(
Expand Down
39 changes: 39 additions & 0 deletions internal/iox/enum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
Copyright 2022 The GoPlus Authors (goplus.org)
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 iox

import (
"bufio"
"io"
)

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

type LineIter struct {
*bufio.Scanner
}

func (it LineIter) Next() (line string, ok bool) {
if ok = it.Scan(); ok {
line = it.Text()
}
return
}

func EnumLines(r io.Reader) LineIter {
scanner := bufio.NewScanner(r)
return LineIter{scanner}
}

// ----------------------------------------------------------------------------
4 changes: 4 additions & 0 deletions package.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ var (
debugImport bool
debugComments bool
debugWriteFile bool
debugImportIox bool
)

func SetDebug(dbgFlags int) {
Expand Down Expand Up @@ -100,6 +101,9 @@ type Config struct {
// DefaultGoFile specifies default file name. It can be empty.
DefaultGoFile string

// PkgPathIox specifies package path of github.com/goplus/gop/builtin/iox
PkgPathIox string

// NewBuiltin is to create the builin package.
NewBuiltin func(pkg *Package, conf *Config) *types.Package

Expand Down
49 changes: 29 additions & 20 deletions stmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ func (p *forStmt) End(cb *CodeBuilder) {
type forRangeStmt struct {
names []string
stmt *ast.RangeStmt
x *internal.Elem
old codeBlockCtx
kvt []types.Type
udt int // 0: non-udt, 2: (elem,ok), 3: (key,elem,ok)
Expand Down Expand Up @@ -480,6 +481,9 @@ func (p *forRangeStmt) RangeAssignThen(cb *CodeBuilder, pos token.Pos) {
log.Panicln("TODO: variable already defined -", name)
}
}
if p.udt != 0 {
p.x = x
}
p.stmt = &ast.RangeStmt{
Key: ident(names[0]),
Value: val,
Expand All @@ -501,16 +505,19 @@ func (p *forRangeStmt) RangeAssignThen(cb *CodeBuilder, pos token.Pos) {
cb.panicCodePosError(pos, "too many variables in range")
}
cb.stk.PopN(n)
p.stmt = &ast.RangeStmt{
Key: key.Val,
Value: val.Val,
X: x.Val,
}
typs := p.getKeyValTypes(cb, x.Type)
if typs == nil {
src, _ := cb.loadExpr(x.Src)
cb.panicCodePosErrorf(pos, "cannot range over %v (type %v)", src, x.Type)
}
if p.udt != 0 {
p.x = &x
}
p.stmt = &ast.RangeStmt{
Key: key.Val,
Value: val.Val,
X: x.Val,
}
if n > 1 {
p.stmt.Tok = token.ASSIGN
checkAssign(cb.pkg, &key, typs[0], "range")
Expand All @@ -536,7 +543,7 @@ retry:
case *types.Array:
return []types.Type{types.Typ[types.Int], e.Elem()}
case *types.Named:
if kv, ok := p.checkUdt(e); ok {
if kv, ok := p.checkUdt(cb, e); ok {
return kv
}
}
Expand All @@ -547,7 +554,7 @@ retry:
return []types.Type{types.Typ[types.Int], types.Typ[types.Rune]}
}
case *types.Named:
if kv, ok := p.checkUdt(t); ok {
if kv, ok := p.checkUdt(cb, t); ok {
return kv
}
typ = cb.getUnderlying(t)
Expand All @@ -556,9 +563,8 @@ retry:
return nil
}

func (p *forRangeStmt) checkUdt(o *types.Named) ([]types.Type, bool) {
if m := findMethod(o, "Gop_Enum"); m != nil {
sig := m.Type().(*types.Signature)
func (p *forRangeStmt) checkUdt(cb *CodeBuilder, o *types.Named) ([]types.Type, bool) {
if sig := findMethodType(cb, o, nameGopEnum); sig != nil {
enumRet := sig.Results()
params := sig.Params()
switch params.Len() {
Expand Down Expand Up @@ -594,8 +600,8 @@ func (p *forRangeStmt) checkUdt(o *types.Named) ([]types.Type, bool) {
typ = t.Elem()
}
if it, ok := typ.(*types.Named); ok {
if next := findMethod(it, "Next"); next != nil {
ret := next.Type().(*types.Signature).Results()
if next := findMethodType(cb, it, "Next"); next != nil {
ret := next.Results()
typs := make([]types.Type, 2)
n := ret.Len()
switch n {
Expand All @@ -617,13 +623,16 @@ func (p *forRangeStmt) checkUdt(o *types.Named) ([]types.Type, bool) {
return nil, false
}

func findMethod(o *types.Named, name string) types.Object {
func findMethodType(cb *CodeBuilder, o *types.Named, name string) mthdSignature {
for i, n := 0, o.NumMethods(); i < n; i++ {
method := o.Method(i)
if method.Name() == name {
return method
return method.Type().(*types.Signature)
}
}
if bti := cb.getBuiltinTI(o); bti != nil {
return bti.lookupByName(name)
}
return nil
}

Expand All @@ -641,6 +650,9 @@ func (p *forRangeStmt) End(cb *CodeBuilder) {
p.stmt.Body = p.handleFor(&ast.BlockStmt{List: stmts}, 1)
cb.emitStmt(p.stmt)
} else if n > 0 {
cb.stk.Push(p.x)
cb.MemberVal(nameGopEnum).Call(0)
callEnum := cb.stk.Pop().Val
/*
for _gop_it := X.Gop_Enum();; {
var _gop_ok bool
Expand Down Expand Up @@ -682,11 +694,7 @@ func (p *forRangeStmt) End(cb *CodeBuilder) {
Init: &ast.AssignStmt{
Lhs: []ast.Expr{identGopIt},
Tok: token.DEFINE,
Rhs: []ast.Expr{
&ast.CallExpr{
Fun: &ast.SelectorExpr{X: p.stmt.X, Sel: identGopEnum},
},
},
Rhs: []ast.Expr{callEnum},
},
Body: p.handleFor(&ast.BlockStmt{List: body}, 2),
}
Expand Down Expand Up @@ -733,9 +741,10 @@ func (p *forRangeStmt) End(cb *CodeBuilder) {
}

var (
nameGopEnum = "Gop_Enum"
identGopOk = ident("_gop_ok")
identGopIt = ident("_gop_it")
identGopEnum = ident("Gop_Enum")
identGopEnum = ident(nameGopEnum)
)

var (
Expand Down