Repository: goplus/gop
Pull request #1157 (2022-04-23 03:41:11)

v1.1.0-beta3
new features:
- cl: [config add NoAutoGenMain for auto generate main func if no entry](https://github.com/goplus/gop/commit/895ad6cad53c26efb84a27496e150d078e81597b)
- builtin: [add type alias any = interface{}](https://github.com/goplus/gop/commit/c7ec8873ee5739d7bfa60f9c0755a5fc72edfe15)
- builtin: [uint128](https://github.com/goplus/gop/commit/f9085bb20617f08c0424a13d4e6f9d38d7c09030), [int128](https://github.com/goplus/gop/commit/09ac6aba9759c601cb15a03edbce75434b11d505)
- ast: [togo.ASTFile](https://github.com/goplus/gop/commit/35a780db62b35d1b88b06bcf3b83859d8c625f00)
- cmd: [add cmd/gopdecl](https://github.com/goplus/gop/commit/ad0d6521c554ae1ea49229757d48ccad33239e53)

changes:
- parser: [fix parserCallOrConversion cmd single line](https://github.com/goplus/gop/commit/882407eb077b0c2ae16050794621f36e8da0d10c)
- cl: [toStructType check name redeclared](https://github.com/goplus/gop/commit/27e36c0ff016a2f6ce69c061d49f7099ea423077)
- cl: [bug fixed // import builtin](https://github.com/goplus/gop/commit/80fe58876f4a0c0cb82d7fdfc2984031254f22e8)
- cl: [cb.Comments => cb.BackupComments](https://github.com/goplus/gop/commit/92954a48c65389002042bca24bcfdeaf47de1d7f)
- x/gopproj: [fix gop run workdir](https://github.com/goplus/gop/commit/cd92edd6eae8da32f1af35e6d798d2d8743dbe94)
- x/gopproj: [handleEvent OnErr no dump error](https://github.com/goplus/gop/commit/d5e4a0554b001c0a3c4c1f17379c13fe3b8ff4cf)
- x/gopproj: [genDefaultGopMod bugfix](https://github.com/goplus/gop/commit/53d7923433814e3e3a0652145a3a999ce8e8fd22)



Patch:
diff --git a/.gitignore b/.gitignore
index 227a0dec..ffb5ce04 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,6 +33,8 @@ coverage.txt
 .gop/
 gop_autogen*.go
 go.json
+go.work
+go.work.sum
 format.result
 *.cache
 
@@ -46,3 +48,5 @@ format.result
 cmd/qfmt/qfmt
 
 bin/
+go-num/
+_todo*.go
diff --git a/README.md b/README.md
index f39559a7..0b705db1 100644
--- a/README.md
+++ b/README.md
@@ -194,25 +194,42 @@ var y *big.Rat = 4/5r
 println x, y
 ```
 
+### Large number: uint128, int128
+
+```go
+var x uint128 = 1 << 65
+var y = x + 1
+println x // output: 36893488147419103232
+println y // output: 36893488147419103233
+```
+
+### Convert bool to number types
+
+```go
+println int(true)       // output: 1
+println float64(true)   // output: 1
+println complex64(true) // output: (1+0i)
+```
+
 ### Map literal
 
 ```go
-x := {"Hello": 1, "xsw": 3.4} // map[string]float64
+x := {"Hello": 1, "xsw": 3.4}   // map[string]float64
 y := {"Hello": 1, "xsw": "Go+"} // map[string]interface{}
-z := {"Hello": 1, "xsw": 3} // map[string]int
-empty := {} // map[string]interface{}
+z := {"Hello": 1, "xsw": 3}     // map[string]int
+empty := {}                     // map[string]interface{}
 ```
 
 ### Slice literal
 
 ```go
-x := [1, 3.4] // []float64
-y := [1] // []int
-z := [1+2i, "xsw"] // []interface{}
+x := [1, 3.4]       // []float64
+y := [1]            // []int
+z := [1+2i, "xsw"]  // []interface{}
 a := [1, 3.4, 3+4i] // []complex128
-b := [5+6i] // []complex128
-c := ["xsw", 3] // []interface{}
-empty := [] // []interface{}
+b := [5+6i]         // []complex128
+c := ["xsw", 3]     // []interface{}
+empty := []         // []interface{}
 ```
 
 ### Lambda expression
diff --git a/VERSION b/VERSION
deleted file mode 100644
index 22ee13bd..00000000
--- a/VERSION
+++ /dev/null
@@ -1,1 +0,0 @@
-v1.1.0-beta2
\ No newline at end of file
diff --git a/ast/ast.go b/ast/ast.go
index 08526d91..9c74b4e0 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -1133,10 +1133,11 @@ func (f *File) End() token.Pos {
 // collectively building a Go package.
 //
 type Package struct {
-	Name    string             // package name
-	Scope   *Scope             // package scope across all files
-	Imports map[string]*Object // map of package id -> package object
-	Files   map[string]*File   // Go source files by filename
+	Name    string               // package name
+	Scope   *Scope               // package scope across all files
+	Imports map[string]*Object   // map of package id -> package object
+	Files   map[string]*File     // Go+ source files by filename
+	GoFiles map[string]*ast.File // Go source files by filename
 }
 
 // Pos returns position of first character belonging to the node.
diff --git a/ast/gopq/gop_query.go b/ast/gopq/gop_query.go
index 65ee3e88..281ed6e0 100644
--- a/ast/gopq/gop_query.go
+++ b/ast/gopq/gop_query.go
@@ -18,7 +18,7 @@ package gopq
 
 import (
 	"errors"
-	"os"
+	"io/fs"
 	"syscall"
 
 	"github.com/goplus/gop/ast"
@@ -59,7 +59,7 @@ type NodeSet struct {
 // directory specified by path and returns a map of package name -> package
 // AST with all the packages found.
 //
-// If filter != nil, only the files with os.FileInfo entries passing through
+// If filter != nil, only the files with fs.FileInfo entries passing through
 // the filter (and ending in ".gop") are considered. The mode bits are passed
 // to ParseFile unchanged. Position information is recorded in fset, which
 // must not be nil.
@@ -69,7 +69,7 @@ type NodeSet struct {
 // first error encountered are returned.
 func NewSource(
 	fset *token.FileSet, path string,
-	filter func(os.FileInfo) bool, mode parser.Mode) (doc NodeSet, err error) {
+	filter func(fs.FileInfo) bool, mode parser.Mode) (doc NodeSet, err error) {
 
 	pkgs, err := parser.ParseDir(fset, path, filter, mode)
 	if err != nil {
@@ -82,7 +82,7 @@ func NewSource(
 // directory specified by path and returns a map of package name -> package
 // AST with all the packages found.
 //
-// If filter != nil, only the files with os.FileInfo entries passing through
+// If filter != nil, only the files with fs.FileInfo entries passing through
 // the filter (and ending in ".gop") are considered. The mode bits are passed
 // to ParseFile unchanged. Position information is recorded in fset, which
 // must not be nil.
@@ -92,7 +92,7 @@ func NewSource(
 // first error encountered are returned.
 func NewSourceFrom(
 	fset *token.FileSet, fs parser.FileSystem, path string,
-	filter func(os.FileInfo) bool, mode parser.Mode) (doc NodeSet, err error) {
+	filter func(fs.FileInfo) bool, mode parser.Mode) (doc NodeSet, err error) {
 
 	pkgs, err := parser.ParseFSDir(fset, fs, path, parser.Config{Filter: filter, Mode: mode})
 	if err != nil {
diff --git a/ast/resolve.go b/ast/resolve.go
index 437f35e8..b796d213 100644
--- a/ast/resolve.go
+++ b/ast/resolve.go
@@ -183,5 +183,5 @@ func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer,
 	}
 
 	p.errors.Sort()
-	return &Package{pkgName, pkgScope, imports, files}, p.errors.Err()
+	return &Package{pkgName, pkgScope, imports, files, nil}, p.errors.Err()
 }
diff --git a/ast/togo/goast.go b/ast/togo/goast.go
new file mode 100644
index 00000000..f8ec8060
--- /dev/null
+++ b/ast/togo/goast.go
@@ -0,0 +1,316 @@
+package togo
+
+import (
+	"go/ast"
+	"go/token"
+	"log"
+	"reflect"
+
+	gopast "github.com/goplus/gop/ast"
+	goptoken "github.com/goplus/gop/token"
+)
+
+// ----------------------------------------------------------------------------
+
+func goExpr(val gopast.Expr) ast.Expr {
+	if val == nil {
+		return nil
+	}
+	switch v := val.(type) {
+	case *gopast.Ident:
+		return goIdent(v)
+	case *gopast.SelectorExpr:
+		return &ast.SelectorExpr{
+			X:   goExpr(v.X),
+			Sel: goIdent(v.Sel),
+		}
+	case *gopast.SliceExpr:
+		return &ast.SliceExpr{
+			X:      goExpr(v.X),
+			Lbrack: v.Lbrack,
+			Low:    goExpr(v.Low),
+			High:   goExpr(v.High),
+			Max:    goExpr(v.Max),
+			Slice3: v.Slice3,
+			Rbrack: v.Rbrack,
+		}
+	case *gopast.StarExpr:
+		return &ast.StarExpr{
+			Star: v.Star,
+			X:    goExpr(v.X),
+		}
+	case *gopast.MapType:
+		return &ast.MapType{
+			Map:   v.Map,
+			Key:   goType(v.Key),
+			Value: goType(v.Value),
+		}
+	case *gopast.StructType:
+		return &ast.StructType{
+			Struct: v.Struct,
+			Fields: goFieldList(v.Fields),
+		}
+	case *gopast.FuncType:
+		return goFuncType(v)
+	case *gopast.InterfaceType:
+		return &ast.InterfaceType{
+			Interface: v.Interface,
+			Methods:   goFieldList(v.Methods),
+		}
+	case *gopast.ArrayType:
+		return &ast.ArrayType{
+			Lbrack: v.Lbrack,
+			Len:    goExpr(v.Len),
+			Elt:    goType(v.Elt),
+		}
+	case *gopast.ChanType:
+		return &ast.ChanType{
+			Begin: v.Begin,
+			Arrow: v.Arrow,
+			Dir:   ast.ChanDir(v.Dir),
+			Value: goType(v.Value),
+		}
+	case *gopast.BasicLit:
+		return goBasicLit(v)
+	case *gopast.BinaryExpr:
+		return &ast.BinaryExpr{
+			X:     goExpr(v.X),
+			OpPos: v.OpPos,
+			Op:    token.Token(v.Op),
+			Y:     goExpr(v.Y),
+		}
+	case *gopast.UnaryExpr:
+		return &ast.UnaryExpr{
+			OpPos: v.OpPos,
+			Op:    token.Token(v.Op),
+			X:     goExpr(v.X),
+		}
+	case *gopast.CallExpr:
+		return &ast.CallExpr{
+			Fun:      goExpr(v.Fun),
+			Lparen:   v.Lparen,
+			Args:     goExprs(v.Args),
+			Ellipsis: v.Ellipsis,
+			Rparen:   v.Rparen,
+		}
+	case *gopast.IndexExpr:
+		return &ast.IndexExpr{
+			X:      goExpr(v.X),
+			Lbrack: v.Lbrack,
+			Index:  goExpr(v.Index),
+			Rbrack: v.Rbrack,
+		}
+	case *gopast.ParenExpr:
+		return &ast.ParenExpr{
+			Lparen: v.Lparen,
+			X:      goExpr(v.X),
+			Rparen: v.Rparen,
+		}
+	case *gopast.CompositeLit:
+		return &ast.CompositeLit{
+			Type:   goType(v.Type),
+			Lbrace: v.Lbrace,
+			Elts:   goExprs(v.Elts),
+			Rbrace: v.Rbrace,
+		}
+	case *gopast.FuncLit:
+		return &ast.FuncLit{
+			Type: goFuncType(v.Type),
+			Body: nil,
+		}
+	case *gopast.TypeAssertExpr:
+		return &ast.TypeAssertExpr{
+			X:      goExpr(v.X),
+			Lparen: v.Lparen,
+			Type:   goType(v.Type),
+			Rparen: v.Rparen,
+		}
+	case *gopast.KeyValueExpr:
+		return &ast.KeyValueExpr{
+			Key:   goExpr(v.Key),
+			Colon: v.Colon,
+			Value: goExpr(v.Value),
+		}
+	case *gopast.Ellipsis:
+		return &ast.Ellipsis{
+			Ellipsis: v.Ellipsis,
+			Elt:      goExpr(v.Elt),
+		}
+	}
+	log.Panicln("goExpr: unknown expr -", reflect.TypeOf(val))
+	return nil
+}
+
+func goExprs(vals []gopast.Expr) []ast.Expr {
+	n := len(vals)
+	if n == 0 {
+		return nil
+	}
+	ret := make([]ast.Expr, n)
+	for i, v := range vals {
+		ret[i] = goExpr(v)
+	}
+	return ret
+}
+
+// ----------------------------------------------------------------------------
+
+func goFuncType(v *gopast.FuncType) *ast.FuncType {
+	return &ast.FuncType{
+		Func:    v.Func,
+		Params:  goFieldList(v.Params),
+		Results: goFieldList(v.Results),
+	}
+}
+
+func goType(v gopast.Expr) ast.Expr {
+	return goExpr(v)
+}
+
+func goBasicLit(v *gopast.BasicLit) *ast.BasicLit {
+	if v == nil {
+		return nil
+	}
+	return &ast.BasicLit{
+		ValuePos: v.ValuePos,
+		Kind:     token.Token(v.Kind),
+		Value:    v.Value,
+	}
+}
+
+func goIdent(v *gopast.Ident) *ast.Ident {
+	if v == nil {
+		return nil
+	}
+	return &ast.Ident{
+		NamePos: v.NamePos,
+		Name:    v.Name,
+	}
+}
+
+func goIdents(names []*gopast.Ident) []*ast.Ident {
+	ret := make([]*ast.Ident, len(names))
+	for i, v := range names {
+		ret[i] = goIdent(v)
+	}
+	return ret
+}
+
+// ----------------------------------------------------------------------------
+
+func goField(v *gopast.Field) *ast.Field {
+	return &ast.Field{
+		Names: goIdents(v.Names),
+		Type:  goType(v.Type),
+		Tag:   goBasicLit(v.Tag),
+	}
+}
+
+func goFieldList(v *gopast.FieldList) *ast.FieldList {
+	if v == nil {
+		return nil
+	}
+	list := make([]*ast.Field, len(v.List))
+	for i, item := range v.List {
+		list[i] = goField(item)
+	}
+	return &ast.FieldList{Opening: v.Opening, List: list, Closing: v.Closing}
+}
+
+func goFuncDecl(v *gopast.FuncDecl) *ast.FuncDecl {
+	return &ast.FuncDecl{
+		Recv: goFieldList(v.Recv),
+		Name: goIdent(v.Name),
+		Type: goFuncType(v.Type),
+		Body: &ast.BlockStmt{}, // ignore function body
+	}
+}
+
+// ----------------------------------------------------------------------------
+
+func goImportSpec(spec *gopast.ImportSpec) *ast.ImportSpec {
+	return &ast.ImportSpec{
+		Name:   goIdent(spec.Name),
+		Path:   goBasicLit(spec.Path),
+		EndPos: spec.EndPos,
+	}
+}
+
+func goTypeSpec(spec *gopast.TypeSpec) *ast.TypeSpec {
+	return &ast.TypeSpec{
+		Name:   goIdent(spec.Name),
+		Assign: spec.Assign,
+		Type:   goType(spec.Type),
+	}
+}
+
+func goValueSpec(spec *gopast.ValueSpec) *ast.ValueSpec {
+	return &ast.ValueSpec{
+		Names:  goIdents(spec.Names),
+		Type:   goType(spec.Type),
+		Values: goExprs(spec.Values),
+	}
+}
+
+func goGenDecl(v *gopast.GenDecl) *ast.GenDecl {
+	specs := make([]ast.Spec, len(v.Specs))
+	for i, spec := range v.Specs {
+		switch v.Tok {
+		case goptoken.IMPORT:
+			specs[i] = goImportSpec(spec.(*gopast.ImportSpec))
+		case goptoken.TYPE:
+			specs[i] = goTypeSpec(spec.(*gopast.TypeSpec))
+		case goptoken.VAR, goptoken.CONST:
+			specs[i] = goValueSpec(spec.(*gopast.ValueSpec))
+		default:
+			log.Panicln("goGenDecl: unknown spec -", v.Tok)
+		}
+	}
+	return &ast.GenDecl{
+		TokPos: v.TokPos,
+		Tok:    token.Token(v.Tok),
+		Lparen: v.Lparen,
+		Specs:  specs,
+		Rparen: v.Rparen,
+	}
+}
+
+// ----------------------------------------------------------------------------
+
+func goDecl(decl gopast.Decl) ast.Decl {
+	switch v := decl.(type) {
+	case *gopast.GenDecl:
+		return goGenDecl(v)
+	case *gopast.FuncDecl:
+		return goFuncDecl(v)
+	}
+	log.Panicln("goDecl: unkown decl -", reflect.TypeOf(decl))
+	return nil
+}
+
+func goDecls(decls []gopast.Decl) []ast.Decl {
+	ret := make([]ast.Decl, len(decls))
+	for i, decl := range decls {
+		ret[i] = goDecl(decl)
+	}
+	return ret
+}
+
+// ----------------------------------------------------------------------------
+
+const (
+	KeepFuncBody = 1 << iota
+)
+
+func ASTFile(f *gopast.File, mode int) *ast.File {
+	if (mode & KeepFuncBody) != 0 {
+		log.Panicln("ASTFile: doesn't support keep func body now")
+	}
+	return &ast.File{
+		Package: f.Package,
+		Name:    goIdent(f.Name),
+		Decls:   goDecls(f.Decls),
+	}
+}
+
+// ----------------------------------------------------------------------------
diff --git a/builtin/big.go b/builtin/big.go
deleted file mode 100644
index 6e83bbac..00000000
--- a/builtin/big.go
+++ /dev/null
@@ -1,485 +0,0 @@
-/*
- * Copyright (c) 2021 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 builtin
-
-import (
-	"math/big"
-)
-
-const (
-	GopPackage = true // to indicate this is a Go+ package
-)
-
-//
-// Gop_: Go+ object prefix
-// Gop_xxx_Cast: type Gop_xxx typecast
-// xxxx__N: the Nth overload function
-//
-
-type Gop_ninteger = uint
-
-func Gop_istmp(a interface{}) bool {
-	return false
-}
-
-// -----------------------------------------------------------------------------
-
-type Gop_untyped_bigint *big.Int
-type Gop_untyped_bigrat *big.Rat
-type Gop_untyped_bigfloat *big.Float
-
-type Gop_untyped_bigint_Default = Gop_bigint
-type Gop_untyped_bigrat_Default = Gop_bigrat
-type Gop_untyped_bigfloat_Default = Gop_bigfloat
-
-func Gop_untyped_bigint_Init__0(x int) Gop_untyped_bigint {
-	panic("make compiler happy")
-}
-
-func Gop_untyped_bigrat_Init__0(x int) Gop_untyped_bigrat {
-	panic("make compiler happy")
-}
-
-func Gop_untyped_bigrat_Init__1(x Gop_untyped_bigint) Gop_untyped_bigrat {
-	panic("make compiler happy")
-}
-
-// -----------------------------------------------------------------------------
-// type bigint
-
-// A Gop_bigint represents a signed multi-precision integer.
-// The zero value for a Gop_bigint represents nil.
-type Gop_bigint struct {
-	*big.Int
-}
-
-func tmpint(a, b Gop_bigint) Gop_bigint {
-	if Gop_istmp(a) {
-		return a
-	} else if Gop_istmp(b) {
-		return b
-	}
-	return Gop_bigint{new(big.Int)}
-}
-
-func tmpint1(a Gop_bigint) Gop_bigint {
-	if Gop_istmp(a) {
-		return a
-	}
-	return Gop_bigint{new(big.Int)}
-}
-
-// IsNil returns a bigint object is nil or not
-func (a Gop_bigint) IsNil() bool {
-	return a.Int == nil
-}
-
-// Gop_Assign: func (a bigint) = (b bigint)
-func (a Gop_bigint) Gop_Assign(b Gop_bigint) {
-	if Gop_istmp(b) {
-		*a.Int = *b.Int
-	} else {
-		a.Int.Set(b.Int)
-	}
-}
-
-// Gop_Add: func (a bigint) + (b bigint) bigint
-func (a Gop_bigint) Gop_Add(b Gop_bigint) Gop_bigint {
-	return Gop_bigint{tmpint(a, b).Add(a.Int, b.Int)}
-}
-
-// Gop_Sub: func (a bigint) - (b bigint) bigint
-func (a Gop_bigint) Gop_Sub(b Gop_bigint) Gop_bigint {
-	return Gop_bigint{tmpint(a, b).Sub(a.Int, b.Int)}
-}
-
-// Gop_Mul: func (a bigint) * (b bigint) bigint
-func (a Gop_bigint) Gop_Mul(b Gop_bigint) Gop_bigint {
-	return Gop_bigint{tmpint(a, b).Mul(a.Int, b.Int)}
-}
-
-// Gop_Quo: func (a bigint) / (b bigint) bigint {
-func (a Gop_bigint) Gop_Quo(b Gop_bigint) Gop_bigint {
-	return Gop_bigint{tmpint(a, b).Quo(a.Int, b.Int)}
-}
-
-// Gop_Rem: func (a bigint) % (b bigint) bigint
-func (a Gop_bigint) Gop_Rem(b Gop_bigint) Gop_bigint {
-	return Gop_bigint{tmpint(a, b).Rem(a.Int, b.Int)}
-}
-
-// Gop_Or: func (a bigint) | (b bigint) bigint
-func (a Gop_bigint) Gop_Or(b Gop_bigint) Gop_bigint {
-	return Gop_bigint{tmpint(a, b).Or(a.Int, b.Int)}
-}
-
-// Gop_Xor: func (a bigint) ^ (b bigint) bigint
-func (a Gop_bigint) Gop_Xor(b Gop_bigint) Gop_bigint {
-	return Gop_bigint{tmpint(a, b).Xor(a.Int, b.Int)}
-}
-
-// Gop_And: func (a bigint) & (b bigint) bigint
-func (a Gop_bigint) Gop_And(b Gop_bigint) Gop_bigint {
-	return Gop_bigint{tmpint(a, b).And(a.Int, b.Int)}
-}
-
-// Gop_AndNot: func (a bigint) &^ (b bigint) bigint
-func (a Gop_bigint) Gop_AndNot(b Gop_bigint) Gop_bigint {
-	return Gop_bigint{tmpint(a, b).AndNot(a.Int, b.Int)}
-}
-
-// Gop_Lsh: func (a bigint) << (n untyped_uint) bigint
-func (a Gop_bigint) Gop_Lsh(n Gop_ninteger) Gop_bigint {
-	return Gop_bigint{tmpint1(a).Lsh(a.Int, uint(n))}
-}
-
-// Gop_Rsh: func (a bigint) >> (n untyped_uint) bigint
-func (a Gop_bigint) Gop_Rsh(n Gop_ninteger) Gop_bigint {
-	return Gop_bigint{tmpint1(a).Rsh(a.Int, uint(n))}
-}
-
-// Gop_LT: func (a bigint) < (b bigint) bool
-func (a Gop_bigint) Gop_LT(b Gop_bigint) bool {
-	return a.Cmp(b.Int) < 0
-}
-
-// Gop_LE: func (a bigint) <= (b bigint) bool
-func (a Gop_bigint) Gop_LE(b Gop_bigint) bool {
-	return a.Cmp(b.Int) <= 0
-}
-
-// Gop_GT: func (a bigint) > (b bigint) bool
-func (a Gop_bigint) Gop_GT(b Gop_bigint) bool {
-	return a.Cmp(b.Int) > 0
-}
-
-// Gop_GE: func (a bigint) >= (b bigint) bool
-func (a Gop_bigint) Gop_GE(b Gop_bigint) bool {
-	return a.Cmp(b.Int) >= 0
-}
-
-// Gop_EQ: func (a bigint) == (b bigint) bool
-func (a Gop_bigint) Gop_EQ(b Gop_bigint) bool {
-	return a.Cmp(b.Int) == 0
-}
-
-// Gop_NE: func (a bigint) != (b bigint) bool
-func (a Gop_bigint) Gop_NE(b Gop_bigint) bool {
-	return a.Cmp(b.Int) != 0
-}
-
-// Gop_Neg: func -(a bigint) bigint
-func (a Gop_bigint) Gop_Neg() Gop_bigint {
-	return Gop_bigint{tmpint1(a).Neg(a.Int)}
-}
-
-// Gop_Pos: func +(a bigint) bigint
-func (a Gop_bigint) Gop_Pos() Gop_bigint {
-	return a
-}
-
-// Gop_Not: func ^(a bigint) bigint
-func (a Gop_bigint) Gop_Not() Gop_bigint {
-	return Gop_bigint{tmpint1(a).Not(a.Int)}
-}
-
-// Gop_Add: func (a bigint) += (b bigint)
-func (a Gop_bigint) Gop_AddAssign(b Gop_bigint) {
-	a.Int.Add(a.Int, b.Int)
-}
-
-// Gop_Sub: func (a bigint) -= (b bigint)
-func (a Gop_bigint) Gop_SubAssign(b Gop_bigint) {
-	a.Int.Sub(a.Int, b.Int)
-}
-
-// Gop_Mul: func (a bigint) *= (b bigint)
-func (a Gop_bigint) Gop_MulAssign(b Gop_bigint) {
-	a.Int.Mul(a.Int, b.Int)
-}
-
-// Gop_Quo: func (a bigint) /= (b bigint) {
-func (a Gop_bigint) Gop_QuoAssign(b Gop_bigint) {
-	a.Int.Quo(a.Int, b.Int)
-}
-
-// Gop_Rem: func (a bigint) %= (b bigint)
-func (a Gop_bigint) Gop_RemAssign(b Gop_bigint) {
-	a.Int.Rem(a.Int, b.Int)
-}
-
-// Gop_Or: func (a bigint) |= (b bigint)
-func (a Gop_bigint) Gop_OrAssign(b Gop_bigint) {
-	a.Int.Or(a.Int, b.Int)
-}
-
-// Gop_Xor: func (a bigint) ^= (b bigint)
-func (a Gop_bigint) Gop_XorAssign(b Gop_bigint) {
-	a.Int.Xor(a.Int, b.Int)
-}
-
-// Gop_And: func (a bigint) &= (b bigint)
-func (a Gop_bigint) Gop_AndAssign(b Gop_bigint) {
-	a.Int.And(a.Int, b.Int)
-}
-
-// Gop_AndNot: func (a bigint) &^= (b bigint)
-func (a Gop_bigint) Gop_AndNotAssign(b Gop_bigint) {
-	a.Int.AndNot(a.Int, b.Int)
-}
-
-// Gop_Lsh: func (a bigint) <<= (n untyped_uint)
-func (a Gop_bigint) Gop_LshAssign(n Gop_ninteger) {
-	a.Int.Lsh(a.Int, uint(n))
-}
-
-// Gop_Rsh: func (a bigint) >>= (n untyped_uint)
-func (a Gop_bigint) Gop_RshAssign(n Gop_ninteger) {
-	a.Int.Rsh(a.Int, uint(n))
-}
-
-// Gop_bigint_Cast: func bigint() bigint
-func Gop_bigint_Cast__0() Gop_bigint {
-	return Gop_bigint{new(big.Int)}
-}
-
-// Gop_bigint_Cast: func bigint(x int64) bigint
-func Gop_bigint_Cast__1(x int64) Gop_bigint {
-	return Gop_bigint{big.NewInt(x)}
-}
-
-// Gop_bigint_Cast: func bigint(x int) bigint
-func Gop_bigint_Cast__2(x int) Gop_bigint {
-	return Gop_bigint{big.NewInt(int64(x))}
-}
-
-// Gop_bigint_Cast: func bigint(x uint64) bigint
-func Gop_bigint_Cast__3(x uint64) Gop_bigint {
-	return Gop_bigint{new(big.Int).SetUint64(x)}
-}
-
-// Gop_bigint_Cast: func bigint(x uint) bigint
-func Gop_bigint_Cast__4(x uint) Gop_bigint {
-	return Gop_bigint{new(big.Int).SetUint64(uint64(x))}
-}
-
-// Gop_bigint_Cast: func bigint(x *big.Int) bigint
-func Gop_bigint_Cast__5(x *big.Int) Gop_bigint {
-	return Gop_bigint{x}
-}
-
-// Gop_bigint_Cast: func bigint(x *big.Rat) bigint
-func Gop_bigint_Cast__6(x *big.Rat) Gop_bigint {
-	if x.IsInt() {
-		return Gop_bigint{x.Num()}
-	}
-	ret, _ := new(big.Float).SetRat(x).Int(nil)
-	return Gop_bigint{ret}
-}
-
-// Gop_bigint_Init: func bigint.init(x int) bigint
-func Gop_bigint_Init__0(x int) Gop_bigint {
-	return Gop_bigint{big.NewInt(int64(x))}
-}
-
-// Gop_bigint_Init: func bigint.init(x *big.Int) bigint
-func Gop_bigint_Init__1(x *big.Int) Gop_bigint {
-	return Gop_bigint{x}
-}
-
-// Gop_bigint_Init: func bigint.init(x *big.Rat) bigint
-func Gop_bigint_Init__2(x *big.Rat) Gop_bigint {
-	if x.IsInt() {
-		return Gop_bigint{x.Num()}
-	}
-	panic("TODO: can't init bigint from bigrat")
-}
-
-// -----------------------------------------------------------------------------
-// type bigrat
-
-// A Gop_bigrat represents a quotient a/b of arbitrary precision.
-// The zero value for a Gop_bigrat represents nil.
-type Gop_bigrat struct {
-	*big.Rat
-}
-
-func tmprat(a, b Gop_bigrat) Gop_bigrat {
-	if Gop_istmp(a) {
-		return a
-	} else if Gop_istmp(b) {
-		return b
-	}
-	return Gop_bigrat{new(big.Rat)}
-}
-
-func tmprat1(a Gop_bigrat) Gop_bigrat {
-	if Gop_istmp(a) {
-		return a
-	}
-	return Gop_bigrat{new(big.Rat)}
-}
-
-// IsNil returns a bigrat object is nil or not
-func (a Gop_bigrat) IsNil() bool {
-	return a.Rat == nil
-}
-
-// Gop_Assign: func (a bigrat) = (b bigrat)
-func (a Gop_bigrat) Gop_Assign(b Gop_bigrat) {
-	if Gop_istmp(b) {
-		*a.Rat = *b.Rat
-	} else {
-		a.Rat.Set(b.Rat)
-	}
-}
-
-// Gop_Add: func (a bigrat) + (b bigrat) bigrat
-func (a Gop_bigrat) Gop_Add(b Gop_bigrat) Gop_bigrat {
-	return Gop_bigrat{tmprat(a, b).Add(a.Rat, b.Rat)}
-}
-
-// Gop_Sub: func (a bigrat) - (b bigrat) bigrat
-func (a Gop_bigrat) Gop_Sub(b Gop_bigrat) Gop_bigrat {
-	return Gop_bigrat{tmprat(a, b).Sub(a.Rat, b.Rat)}
-}
-
-// Gop_Mul: func (a bigrat) * (b bigrat) bigrat
-func (a Gop_bigrat) Gop_Mul(b Gop_bigrat) Gop_bigrat {
-	return Gop_bigrat{tmprat(a, b).Mul(a.Rat, b.Rat)}
-}
-
-// Gop_Quo: func (a bigrat) / (b bigrat) bigrat
-func (a Gop_bigrat) Gop_Quo(b Gop_bigrat) Gop_bigrat {
-	return Gop_bigrat{tmprat(a, b).Quo(a.Rat, b.Rat)}
-}
-
-// Gop_LT: func (a bigrat) < (b bigrat) bool
-func (a Gop_bigrat) Gop_LT(b Gop_bigrat) bool {
-	return a.Cmp(b.Rat) < 0
-}
-
-// Gop_LE: func (a bigrat) <= (b bigrat) bool
-func (a Gop_bigrat) Gop_LE(b Gop_bigrat) bool {
-	return a.Cmp(b.Rat) <= 0
-}
-
-// Gop_GT: func (a bigrat) > (b bigrat) bool
-func (a Gop_bigrat) Gop_GT(b Gop_bigrat) bool {
-	return a.Cmp(b.Rat) > 0
-}
-
-// Gop_GE: func (a bigrat) >= (b bigrat) bool
-func (a Gop_bigrat) Gop_GE(b Gop_bigrat) bool {
-	return a.Cmp(b.Rat) >= 0
-}
-
-// Gop_EQ: func (a bigrat) == (b bigrat) bool
-func (a Gop_bigrat) Gop_EQ(b Gop_bigrat) bool {
-	return a.Cmp(b.Rat) == 0
-}
-
-// Gop_NE: func (a bigrat) != (b bigrat) bool
-func (a Gop_bigrat) Gop_NE(b Gop_bigrat) bool {
-	return a.Cmp(b.Rat) != 0
-}
-
-// Gop_Neg: func -(a bigrat) bigrat
-func (a Gop_bigrat) Gop_Neg() Gop_bigrat {
-	return Gop_bigrat{tmprat1(a).Neg(a.Rat)}
-}
-
-// Gop_Pos: func +(a bigrat) bigrat
-func (a Gop_bigrat) Gop_Pos() Gop_bigrat {
-	return a
-}
-
-// Gop_Inv: func /(a bigrat) bigrat
-func (a Gop_bigrat) Gop_Inv() Gop_bigrat {
-	return Gop_bigrat{tmprat1(a).Inv(a.Rat)}
-}
-
-// Gop_Add: func (a bigrat) += (b bigrat)
-func (a Gop_bigrat) Gop_AddAssign(b Gop_bigrat) {
-	a.Rat.Add(a.Rat, b.Rat)
-}
-
-// Gop_Sub: func (a bigrat) -= (b bigrat)
-func (a Gop_bigrat) Gop_SubAssign(b Gop_bigrat) {
-	a.Rat.Sub(a.Rat, b.Rat)
-}
-
-// Gop_Mul: func (a bigrat) *= (b bigrat)
-func (a Gop_bigrat) Gop_MulAssign(b Gop_bigrat) {
-	a.Rat.Mul(a.Rat, b.Rat)
-}
-
-// Gop_Quo: func (a bigrat) /= (b bigrat)
-func (a Gop_bigrat) Gop_QuoAssign(b Gop_bigrat) {
-	a.Rat.Quo(a.Rat, b.Rat)
-}
-
-// Gop_bigrat_Cast: func bigrat() bigrat
-func Gop_bigrat_Cast__0() Gop_bigrat {
-	return Gop_bigrat{new(big.Rat)}
-}
-
-// Gop_bigrat_Cast: func bigrat(a bigint) bigrat
-func Gop_bigrat_Cast__1(a Gop_bigint) Gop_bigrat {
-	return Gop_bigrat{new(big.Rat).SetInt(a.Int)}
-}
-
-// Gop_bigrat_Cast: func bigrat(a *big.Int) bigrat
-func Gop_bigrat_Cast__2(a *big.Int) Gop_bigrat {
-	return Gop_bigrat{new(big.Rat).SetInt(a)}
-}
-
-// Gop_bigrat_Cast: func bigrat(a, b int64) bigrat
-func Gop_bigrat_Cast__3(a, b int64) Gop_bigrat {
-	return Gop_bigrat{big.NewRat(a, b)}
-}
-
-// Gop_bigrat_Cast: func bigrat(a *big.Rat) bigrat
-func Gop_bigrat_Cast__4(a *big.Rat) Gop_bigrat {
-	return Gop_bigrat{a}
-}
-
-// Gop_bigrat_Init: func bigrat.init(x untyped_int) bigrat
-func Gop_bigrat_Init__0(x int) Gop_bigrat {
-	return Gop_bigrat{big.NewRat(int64(x), 1)}
-}
-
-// Gop_bigrat_Init: func bigrat.init(x untyped_bigint) bigrat
-func Gop_bigrat_Init__1(x Gop_untyped_bigint) Gop_bigrat {
-	return Gop_bigrat{new(big.Rat).SetInt(x)}
-}
-
-// Gop_bigrat_Init: func bigrat.init(x *big.Rat) bigrat
-func Gop_bigrat_Init__2(x *big.Rat) Gop_bigrat {
-	return Gop_bigrat{x}
-}
-
-// -----------------------------------------------------------------------------
-// type bigfloat
-
-// A Gop_bigfloat represents a multi-precision floating point number.
-// The zero value for a Gop_bigfloat represents nil.
-type Gop_bigfloat struct {
-	*big.Float
-}
-
-// -----------------------------------------------------------------------------
diff --git a/builtin/_big.gop b/builtin/ng/_big.gop
similarity index 100%
rename from builtin/_big.gop
rename to builtin/ng/_big.gop
diff --git a/builtin/ng/big.go b/builtin/ng/big.go
new file mode 100644
index 00000000..4bb173b1
--- /dev/null
+++ b/builtin/ng/big.go
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 2021 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 ng
+
+import (
+	"math/big"
+)
+
+func Gop_istmp(a interface{}) bool {
+	return false
+}
+
+// -----------------------------------------------------------------------------
+
+type UntypedBigint *big.Int
+type UntypedBigrat *big.Rat
+type UntypedBigfloat *big.Float
+
+type UntypedBigint_Default = Bigint
+type UntypedBigrat_Default = Bigrat
+type UntypedBigfloat_Default = Bigfloat
+
+func UntypedBigint_Init__0(x int) UntypedBigint {
+	panic(makeCompilerHappy)
+}
+
+func UntypedBigrat_Init__0(x int) UntypedBigrat {
+	panic(makeCompilerHappy)
+}
+
+func UntypedBigrat_Init__1(x UntypedBigint) UntypedBigrat {
+	panic(makeCompilerHappy)
+}
+
+const (
+	makeCompilerHappy = "make compiler happy"
+)
+
+// -----------------------------------------------------------------------------
+// type bigint
+
+// A Bigint represents a signed multi-precision integer.
+// The zero value for a Bigint represents nil.
+type Bigint struct {
+	*big.Int
+}
+
+func tmpint(a, b Bigint) Bigint {
+	if Gop_istmp(a) {
+		return a
+	} else if Gop_istmp(b) {
+		return b
+	}
+	return Bigint{new(big.Int)}
+}
+
+func tmpint1(a Bigint) Bigint {
+	if Gop_istmp(a) {
+		return a
+	}
+	return Bigint{new(big.Int)}
+}
+
+// IsNil returns a bigint object is nil or not
+func (a Bigint) IsNil() bool {
+	return a.Int == nil
+}
+
+// Gop_Assign: func (a bigint) = (b bigint)
+func (a Bigint) Gop_Assign(b Bigint) {
+	if Gop_istmp(b) {
+		*a.Int = *b.Int
+	} else {
+		a.Int.Set(b.Int)
+	}
+}
+
+// Gop_Add: func (a bigint) + (b bigint) bigint
+func (a Bigint) Gop_Add(b Bigint) Bigint {
+	return Bigint{tmpint(a, b).Add(a.Int, b.Int)}
+}
+
+// Gop_Sub: func (a bigint) - (b bigint) bigint
+func (a Bigint) Gop_Sub(b Bigint) Bigint {
+	return Bigint{tmpint(a, b).Sub(a.Int, b.Int)}
+}
+
+// Gop_Mul: func (a bigint) * (b bigint) bigint
+func (a Bigint) Gop_Mul(b Bigint) Bigint {
+	return Bigint{tmpint(a, b).Mul(a.Int, b.Int)}
+}
+
+// Gop_Quo: func (a bigint) / (b bigint) bigint {
+func (a Bigint) Gop_Quo(b Bigint) Bigint {
+	return Bigint{tmpint(a, b).Quo(a.Int, b.Int)}
+}
+
+// Gop_Rem: func (a bigint) % (b bigint) bigint
+func (a Bigint) Gop_Rem(b Bigint) Bigint {
+	return Bigint{tmpint(a, b).Rem(a.Int, b.Int)}
+}
+
+// Gop_Or: func (a bigint) | (b bigint) bigint
+func (a Bigint) Gop_Or(b Bigint) Bigint {
+	return Bigint{tmpint(a, b).Or(a.Int, b.Int)}
+}
+
+// Gop_Xor: func (a bigint) ^ (b bigint) bigint
+func (a Bigint) Gop_Xor(b Bigint) Bigint {
+	return Bigint{tmpint(a, b).Xor(a.Int, b.Int)}
+}
+
+// Gop_And: func (a bigint) & (b bigint) bigint
+func (a Bigint) Gop_And(b Bigint) Bigint {
+	return Bigint{tmpint(a, b).And(a.Int, b.Int)}
+}
+
+// Gop_AndNot: func (a bigint) &^ (b bigint) bigint
+func (a Bigint) Gop_AndNot(b Bigint) Bigint {
+	return Bigint{tmpint(a, b).AndNot(a.Int, b.Int)}
+}
+
+// Gop_Lsh: func (a bigint) << (n untyped_uint) bigint
+func (a Bigint) Gop_Lsh(n Gop_ninteger) Bigint {
+	return Bigint{tmpint1(a).Lsh(a.Int, uint(n))}
+}
+
+// Gop_Rsh: func (a bigint) >> (n untyped_uint) bigint
+func (a Bigint) Gop_Rsh(n Gop_ninteger) Bigint {
+	return Bigint{tmpint1(a).Rsh(a.Int, uint(n))}
+}
+
+// Gop_LT: func (a bigint) < (b bigint) bool
+func (a Bigint) Gop_LT(b Bigint) bool {
+	return a.Cmp(b.Int) < 0
+}
+
+// Gop_LE: func (a bigint) <= (b bigint) bool
+func (a Bigint) Gop_LE(b Bigint) bool {
+	return a.Cmp(b.Int) <= 0
+}
+
+// Gop_GT: func (a bigint) > (b bigint) bool
+func (a Bigint) Gop_GT(b Bigint) bool {
+	return a.Cmp(b.Int) > 0
+}
+
+// Gop_GE: func (a bigint) >= (b bigint) bool
+func (a Bigint) Gop_GE(b Bigint) bool {
+	return a.Cmp(b.Int) >= 0
+}
+
+// Gop_EQ: func (a bigint) == (b bigint) bool
+func (a Bigint) Gop_EQ(b Bigint) bool {
+	return a.Cmp(b.Int) == 0
+}
+
+// Gop_NE: func (a bigint) != (b bigint) bool
+func (a Bigint) Gop_NE(b Bigint) bool {
+	return a.Cmp(b.Int) != 0
+}
+
+// Gop_Neg: func -(a bigint) bigint
+func (a Bigint) Gop_Neg() Bigint {
+	return Bigint{tmpint1(a).Neg(a.Int)}
+}
+
+// Gop_Pos: func +(a bigint) bigint
+func (a Bigint) Gop_Pos() Bigint {
+	return a
+}
+
+// Gop_Not: func ^(a bigint) bigint
+func (a Bigint) Gop_Not() Bigint {
+	return Bigint{tmpint1(a).Not(a.Int)}
+}
+
+// Gop_Add: func (a bigint) += (b bigint)
+func (a Bigint) Gop_AddAssign(b Bigint) {
+	a.Int.Add(a.Int, b.Int)
+}
+
+// Gop_Sub: func (a bigint) -= (b bigint)
+func (a Bigint) Gop_SubAssign(b Bigint) {
+	a.Int.Sub(a.Int, b.Int)
+}
+
+// Gop_Mul: func (a bigint) *= (b bigint)
+func (a Bigint) Gop_MulAssign(b Bigint) {
+	a.Int.Mul(a.Int, b.Int)
+}
+
+// Gop_Quo: func (a bigint) /= (b bigint) {
+func (a Bigint) Gop_QuoAssign(b Bigint) {
+	a.Int.Quo(a.Int, b.Int)
+}
+
+// Gop_Rem: func (a bigint) %= (b bigint)
+func (a Bigint) Gop_RemAssign(b Bigint) {
+	a.Int.Rem(a.Int, b.Int)
+}
+
+// Gop_Or: func (a bigint) |= (b bigint)
+func (a Bigint) Gop_OrAssign(b Bigint) {
+	a.Int.Or(a.Int, b.Int)
+}
+
+// Gop_Xor: func (a bigint) ^= (b bigint)
+func (a Bigint) Gop_XorAssign(b Bigint) {
+	a.Int.Xor(a.Int, b.Int)
+}
+
+// Gop_And: func (a bigint) &= (b bigint)
+func (a Bigint) Gop_AndAssign(b Bigint) {
+	a.Int.And(a.Int, b.Int)
+}
+
+// Gop_AndNot: func (a bigint) &^= (b bigint)
+func (a Bigint) Gop_AndNotAssign(b Bigint) {
+	a.Int.AndNot(a.Int, b.Int)
+}
+
+// Gop_Lsh: func (a bigint) <<= (n untyped_uint)
+func (a Bigint) Gop_LshAssign(n Gop_ninteger) {
+	a.Int.Lsh(a.Int, uint(n))
+}
+
+// Gop_Rsh: func (a bigint) >>= (n untyped_uint)
+func (a Bigint) Gop_RshAssign(n Gop_ninteger) {
+	a.Int.Rsh(a.Int, uint(n))
+}
+
+// Bigint_Cast: func bigint(x untyped_int) bigint
+func Bigint_Cast__0(x int) Bigint {
+	return Bigint{big.NewInt(int64(x))}
+}
+
+// Bigint_Cast: func bigint(x untyped_bigint) bigint
+func Bigint_Cast__1(x UntypedBigint) Bigint {
+	return Bigint{x}
+}
+
+// Bigint_Cast: func bigint(x int64) bigint
+func Bigint_Cast__2(x int64) Bigint {
+	return Bigint{big.NewInt(x)}
+}
+
+// Bigint_Cast: func bigint(x uint64) bigint
+func Bigint_Cast__3(x uint64) Bigint {
+	return Bigint{new(big.Int).SetUint64(x)}
+}
+
+// Bigint_Cast: func bigint(x uint) bigint
+func Bigint_Cast__4(x uint) Bigint {
+	return Bigint{new(big.Int).SetUint64(uint64(x))}
+}
+
+// Bigint_Cast: func bigint(x *big.Int) bigint
+func Bigint_Cast__5(x *big.Int) Bigint {
+	return Bigint{x}
+}
+
+// Bigint_Cast: func bigint(x *big.Rat) bigint
+func Bigint_Cast__6(x *big.Rat) Bigint {
+	if x.IsInt() {
+		return Bigint{x.Num()}
+	}
+	ret, _ := new(big.Float).SetRat(x).Int(nil)
+	return Bigint{ret}
+}
+
+// Bigint_Cast: func bigint() bigint
+func Bigint_Cast__7() Bigint {
+	return Bigint{new(big.Int)}
+}
+
+// Bigint_Init: func bigint.init(x int) bigint
+func Bigint_Init__0(x int) Bigint {
+	return Bigint{big.NewInt(int64(x))}
+}
+
+// Bigint_Init: func bigint.init(x untyped_bigint) bigint
+func Bigint_Init__1(x UntypedBigint) Bigint {
+	return Bigint{x}
+}
+
+// Bigint_Init: func bigint.init(x *big.Int) bigint
+func Bigint_Init__2(x *big.Int) Bigint {
+	return Bigint{x}
+}
+
+// -----------------------------------------------------------------------------
+// type bigrat
+
+// A Bigrat represents a quotient a/b of arbitrary precision.
+// The zero value for a Bigrat represents nil.
+type Bigrat struct {
+	*big.Rat
+}
+
+func tmprat(a, b Bigrat) Bigrat {
+	if Gop_istmp(a) {
+		return a
+	} else if Gop_istmp(b) {
+		return b
+	}
+	return Bigrat{new(big.Rat)}
+}
+
+func tmprat1(a Bigrat) Bigrat {
+	if Gop_istmp(a) {
+		return a
+	}
+	return Bigrat{new(big.Rat)}
+}
+
+// IsNil returns a bigrat object is nil or not
+func (a Bigrat) IsNil() bool {
+	return a.Rat == nil
+}
+
+// Gop_Assign: func (a bigrat) = (b bigrat)
+func (a Bigrat) Gop_Assign(b Bigrat) {
+	if Gop_istmp(b) {
+		*a.Rat = *b.Rat
+	} else {
+		a.Rat.Set(b.Rat)
+	}
+}
+
+// Gop_Add: func (a bigrat) + (b bigrat) bigrat
+func (a Bigrat) Gop_Add(b Bigrat) Bigrat {
+	return Bigrat{tmprat(a, b).Add(a.Rat, b.Rat)}
+}
+
+// Gop_Sub: func (a bigrat) - (b bigrat) bigrat
+func (a Bigrat) Gop_Sub(b Bigrat) Bigrat {
+	return Bigrat{tmprat(a, b).Sub(a.Rat, b.Rat)}
+}
+
+// Gop_Mul: func (a bigrat) * (b bigrat) bigrat
+func (a Bigrat) Gop_Mul(b Bigrat) Bigrat {
+	return Bigrat{tmprat(a, b).Mul(a.Rat, b.Rat)}
+}
+
+// Gop_Quo: func (a bigrat) / (b bigrat) bigrat
+func (a Bigrat) Gop_Quo(b Bigrat) Bigrat {
+	return Bigrat{tmprat(a, b).Quo(a.Rat, b.Rat)}
+}
+
+// Gop_LT: func (a bigrat) < (b bigrat) bool
+func (a Bigrat) Gop_LT(b Bigrat) bool {
+	return a.Cmp(b.Rat) < 0
+}
+
+// Gop_LE: func (a bigrat) <= (b bigrat) bool
+func (a Bigrat) Gop_LE(b Bigrat) bool {
+	return a.Cmp(b.Rat) <= 0
+}
+
+// Gop_GT: func (a bigrat) > (b bigrat) bool
+func (a Bigrat) Gop_GT(b Bigrat) bool {
+	return a.Cmp(b.Rat) > 0
+}
+
+// Gop_GE: func (a bigrat) >= (b bigrat) bool
+func (a Bigrat) Gop_GE(b Bigrat) bool {
+	return a.Cmp(b.Rat) >= 0
+}
+
+// Gop_EQ: func (a bigrat) == (b bigrat) bool
+func (a Bigrat) Gop_EQ(b Bigrat) bool {
+	return a.Cmp(b.Rat) == 0
+}
+
+// Gop_NE: func (a bigrat) != (b bigrat) bool
+func (a Bigrat) Gop_NE(b Bigrat) bool {
+	return a.Cmp(b.Rat) != 0
+}
+
+// Gop_Neg: func -(a bigrat) bigrat
+func (a Bigrat) Gop_Neg() Bigrat {
+	return Bigrat{tmprat1(a).Neg(a.Rat)}
+}
+
+// Gop_Pos: func +(a bigrat) bigrat
+func (a Bigrat) Gop_Pos() Bigrat {
+	return a
+}
+
+// Gop_Inv: func /(a bigrat) bigrat
+func (a Bigrat) Gop_Inv() Bigrat {
+	return Bigrat{tmprat1(a).Inv(a.Rat)}
+}
+
+// Gop_Add: func (a bigrat) += (b bigrat)
+func (a Bigrat) Gop_AddAssign(b Bigrat) {
+	a.Rat.Add(a.Rat, b.Rat)
+}
+
+// Gop_Sub: func (a bigrat) -= (b bigrat)
+func (a Bigrat) Gop_SubAssign(b Bigrat) {
+	a.Rat.Sub(a.Rat, b.Rat)
+}
+
+// Gop_Mul: func (a bigrat) *= (b bigrat)
+func (a Bigrat) Gop_MulAssign(b Bigrat) {
+	a.Rat.Mul(a.Rat, b.Rat)
+}
+
+// Gop_Quo: func (a bigrat) /= (b bigrat)
+func (a Bigrat) Gop_QuoAssign(b Bigrat) {
+	a.Rat.Quo(a.Rat, b.Rat)
+}
+
+// Bigrat_Cast: func bigrat(x untyped_int) bigrat
+func Bigrat_Cast__0(x int) Bigrat {
+	return Bigrat{big.NewRat(int64(x), 1)}
+}
+
+// Bigrat_Cast: func bigrat(a untyped_bigint) bigrat
+func Bigrat_Cast__1(a UntypedBigint) Bigrat {
+	return Bigrat{new(big.Rat).SetInt(a)}
+}
+
+// Bigrat_Cast: func bigrat(a *big.Int) bigrat
+func Bigrat_Cast__2(a *big.Int) Bigrat {
+	return Bigrat{new(big.Rat).SetInt(a)}
+}
+
+// Bigrat_Cast: func bigrat(a bigint) bigrat
+func Bigrat_Cast__3(a Bigint) Bigrat {
+	return Bigrat{new(big.Rat).SetInt(a.Int)}
+}
+
+// Bigrat_Cast: func bigrat(a *big.Rat) bigrat
+func Bigrat_Cast__4(a *big.Rat) Bigrat {
+	return Bigrat{a}
+}
+
+// Bigrat_Cast: func bigrat() bigrat
+func Bigrat_Cast__5() Bigrat {
+	return Bigrat{new(big.Rat)}
+}
+
+// Bigrat_Cast: func bigrat(a, b int64) bigrat
+func Bigrat_Cast__6(a, b int64) Bigrat {
+	return Bigrat{big.NewRat(a, b)}
+}
+
+// Bigrat_Init: func bigrat.init(x untyped_int) bigrat
+func Bigrat_Init__0(x int) Bigrat {
+	return Bigrat{big.NewRat(int64(x), 1)}
+}
+
+// Bigrat_Init: func bigrat.init(x untyped_bigint) bigrat
+func Bigrat_Init__1(x UntypedBigint) Bigrat {
+	return Bigrat{new(big.Rat).SetInt(x)}
+}
+
+// Bigrat_Init: func bigrat.init(x *big.Rat) bigrat
+func Bigrat_Init__2(x *big.Rat) Bigrat {
+	return Bigrat{x}
+}
+
+// -----------------------------------------------------------------------------
+// type bigfloat
+
+// A Bigfloat represents a multi-precision floating point number.
+// The zero value for a Float represents nil.
+type Bigfloat struct {
+	*big.Float
+}
+
+// -----------------------------------------------------------------------------
diff --git a/builtin/ng/int128.go b/builtin/ng/int128.go
new file mode 100644
index 00000000..ff586c1a
--- /dev/null
+++ b/builtin/ng/int128.go
@@ -0,0 +1,705 @@
+package ng
+
+import (
+	"fmt"
+	"log"
+	"math/big"
+	"math/bits"
+)
+
+const (
+	signBit  = 0x8000000000000000
+	maxInt64 = 1<<63 - 1
+)
+
+// -----------------------------------------------------------------------------
+
+type Int128 struct {
+	hi uint64
+	lo uint64
+}
+
+// Int128_Init: func int128.init(v int) int128
+func Int128_Init__0(v int) (out Int128) {
+	return Int128_Cast__2(int64(v))
+}
+
+// Int128_Init: func int128.init(v untyped_bigint) int128
+func Int128_Init__1(v UntypedBigint) (out Int128) {
+	return Int128_Cast__1(v)
+}
+
+// Int128_Cast: func int128(v int) int128
+func Int128_Cast__0(v int) Int128 {
+	return Int128_Cast__2(int64(v))
+}
+
+// Int128_Cast: func int128(v untyped_bigint) int128
+func Int128_Cast__1(v UntypedBigint) (out Int128) {
+	out, inRange := Int128_Cast__9(v)
+	if !inRange {
+		log.Panicf("value %v was not in valid int128 range\n", v)
+	}
+	return
+}
+
+// Int128_Cast: func int128(v int64) int128
+func Int128_Cast__2(v int64) (out Int128) {
+	var hi uint64
+	if v < 0 {
+		hi = maxUint64
+	}
+	return Int128{hi: hi, lo: uint64(v)}
+}
+
+// Int128_Cast: func int128(v uint64) int128
+func Int128_Cast__3(v uint64) Int128 {
+	return Int128{lo: v}
+}
+
+// Int128_Cast: func int128(v int32) int128
+func Int128_Cast__4(v int32) Int128 {
+	return Int128_Cast__2(int64(v))
+}
+
+// Int128_Cast: func int128(v int16) int128
+func Int128_Cast__5(v int16) Int128 {
+	return Int128_Cast__2(int64(v))
+}
+
+// Int128_Cast: func int128(v int8) int128
+func Int128_Cast__6(v int8) Int128 {
+	return Int128_Cast__2(int64(v))
+}
+
+// Int128_Cast: func int128(v uint18) int128
+func Int128_Cast__7(v Uint128) (out Int128) {
+	return Int128{hi: v.hi, lo: v.lo}
+}
+
+// Int128_Cast: func int128(v *big.Int) int128
+func Int128_Cast__8(v *big.Int) Int128 {
+	out, _ := Int128_Cast__9(v)
+	return out
+}
+
+func Int128_Cast__9(v *big.Int) (out Int128, inRange bool) {
+	neg := v.Sign() < 0
+	words := v.Bits()
+
+	var u Uint128
+	inRange = true
+	switch intSize {
+	case 64:
+		lw := len(words)
+		switch lw {
+		case 0:
+		case 1:
+			u.lo = uint64(words[0])
+		case 2:
+			u.hi = uint64(words[1])
+			u.lo = uint64(words[0])
+		default:
+			u, inRange = Uint128{hi: maxUint64, lo: maxUint64}, false
+		}
+
+	case 32:
+		lw := len(words)
+		switch lw {
+		case 0:
+		case 1:
+			u.lo = uint64(words[0])
+		case 2:
+			u.lo = (uint64(words[1]) << 32) | (uint64(words[0]))
+		case 3:
+			u.hi = uint64(words[2])
+			u.lo = (uint64(words[1]) << 32) | (uint64(words[0]))
+		case 4:
+			u.hi = (uint64(words[3]) << 32) | (uint64(words[2]))
+			u.lo = (uint64(words[1]) << 32) | (uint64(words[0]))
+		default:
+			u, inRange = Uint128{hi: maxUint64, lo: maxUint64}, false
+		}
+
+	default:
+		panic("unsupported bit size")
+	}
+
+	if neg {
+		if cmp := u.Cmp__1(Uint128{hi: 0x8000000000000000, lo: 0}); cmp > 0 {
+			out, inRange = Int128{hi: 0x8000000000000000, lo: 0}, false
+		} else {
+			out = Int128{hi: u.hi, lo: u.lo}.Gop_Neg()
+		}
+	} else {
+		if cmp := u.Cmp__1(Uint128{hi: maxInt64, lo: maxUint64}); cmp > 0 {
+			out, inRange = Int128{hi: maxInt64, lo: maxUint64}, false
+		} else {
+			out = Int128{hi: u.hi, lo: u.lo}
+		}
+	}
+	return
+}
+
+// Int128_Cast: func int128() int128
+func Int128_Cast__a() Int128 {
+	return Int128{}
+}
+
+// Gop_Rcast: func uint128(v int128) uint128
+func (i Int128) Gop_Rcast__0() Uint128 {
+	return Uint128{lo: i.lo, hi: i.hi}
+}
+
+// Gop_Rcast: func uint128(v int128) (uint128, bool)
+func (i Int128) Gop_Rcast__1() (out Uint128, inRange bool) {
+	return Uint128{lo: i.lo, hi: i.hi}, i.hi&signBit == 0
+}
+
+// Gop_Rcast: func int64(v int128) int64
+func (i Int128) Gop_Rcast__2() int64 {
+	if i.hi&signBit == 0 {
+		return int64(i.lo)
+	}
+	return -int64(^(i.lo - 1))
+}
+
+// Gop_Rcast: func int64(v int128) (int64, bool)
+func (i Int128) Gop_Rcast__3() (out int64, inRange bool) {
+	if i.hi&signBit == 0 {
+		return int64(i.lo), i.hi == 0 && i.lo <= maxInt64
+	}
+	return -int64(^(i.lo - 1)), i.hi == maxUint64 && i.lo >= 0x8000000000000000
+}
+
+// Gop_Rcast: func uint64(v int128) uint64
+func (i Int128) Gop_Rcast__4() uint64 {
+	return i.lo
+}
+
+// Gop_Rcast: func uint64(v int128) (uint64, bool)
+func (i Int128) Gop_Rcast__5() (out uint64, inRange bool) {
+	return i.lo, i.hi == 0
+}
+
+// -----------------------------------------------------------------------------
+
+func (i Int128) IsZero() bool {
+	return i.lo == 0 && i.hi == 0
+}
+
+func (i *Int128) Scan(state fmt.ScanState, verb rune) (err error) {
+	t, err := state.Token(true, nil)
+	if err != nil {
+		return
+	}
+	v, err := ParseInt128(string(t), 10)
+	if err == nil {
+		*i = v
+	}
+	return
+}
+
+func (i Int128) Format(s fmt.State, c rune) {
+	// TODO: not so good
+	i.BigInt().Format(s, c)
+}
+
+func (i Int128) String() string {
+	return i.Text(10)
+}
+
+func (i Int128) Text(base int) string {
+	// TODO: not so good
+	return i.BigInt().Text(base)
+}
+
+func (i Int128) BigInt() *big.Int {
+	var v big.Int
+	i.ToBigInt(&v)
+	return &v
+}
+
+func (i Int128) ToBigInt(b *big.Int) {
+	neg := i.hi&signBit != 0
+	if i.hi > 0 {
+		b.SetUint64(i.hi)
+		b.Lsh(b, 64)
+	}
+	var lo big.Int
+	lo.SetUint64(i.lo)
+	b.Add(b, &lo)
+
+	if neg {
+		b.Xor(b, bigMaxU128).Add(b, big1).Neg(b)
+	}
+}
+
+var (
+	big1          = new(big.Int).SetUint64(1)
+	bigMaxU128, _ = new(big.Int).SetString("340282366920938463463374607431768211455", 10)
+)
+
+func (i Int128) Sign() int {
+	if i.lo == 0 && i.hi == 0 {
+		return 0
+	} else if i.hi&signBit == 0 {
+		return 1
+	}
+	return -1
+}
+
+func (i Int128) Gop_Inc() (v Int128) {
+	v.lo = i.lo + 1
+	v.hi = i.hi
+	if i.lo > v.lo {
+		v.hi++
+	}
+	return v
+}
+
+func (i Int128) Gop_Dec() (v Int128) {
+	v.lo = i.lo - 1
+	v.hi = i.hi
+	if i.lo < v.lo {
+		v.hi--
+	}
+	return v
+}
+
+func (i Int128) Gop_Add__1(n Int128) (v Int128) {
+	var carry uint64
+	v.lo, carry = bits.Add64(i.lo, n.lo, 0)
+	v.hi, _ = bits.Add64(i.hi, n.hi, carry)
+	return v
+}
+
+func (i Int128) Gop_Add__0(n int64) (v Int128) {
+	var carry uint64
+	v.lo, carry = bits.Add64(i.lo, uint64(n), 0)
+	if n < 0 {
+		v.hi = i.hi + maxUint64 + carry
+	} else {
+		v.hi = i.hi + carry
+	}
+	return v
+}
+
+func (i Int128) Gop_Sub__1(n Int128) (v Int128) {
+	var borrowed uint64
+	v.lo, borrowed = bits.Sub64(i.lo, n.lo, 0)
+	v.hi, _ = bits.Sub64(i.hi, n.hi, borrowed)
+	return v
+}
+
+func (i Int128) Gop_Sub__0(n int64) (v Int128) {
+	var borrowed uint64
+	if n < 0 {
+		v.lo, borrowed = bits.Sub64(i.lo, uint64(n), 0)
+		v.hi = i.hi - maxUint64 - borrowed
+	} else {
+		v.lo, borrowed = bits.Sub64(i.lo, uint64(n), 0)
+		v.hi = i.hi - borrowed
+	}
+	return v
+}
+
+func (i Int128) Gop_Neg() (v Int128) {
+	if i.lo == 0 && i.hi == 0 {
+		return
+	}
+	if i.hi&signBit == 0 {
+		v.hi = ^i.hi
+		v.lo = (^i.lo) + 1
+	} else {
+		v.hi = ^i.hi
+		v.lo = ^(i.lo - 1)
+	}
+	if v.lo == 0 { // handle overflow
+		v.hi++
+	}
+	return v
+}
+
+// Abs returns the absolute value of i as a signed integer.
+func (i Int128) Abs__0() Int128 {
+	if i.hi&signBit != 0 {
+		i.hi = ^i.hi
+		i.lo = ^(i.lo - 1)
+		if i.lo == 0 { // handle carry
+			i.hi++
+		}
+	}
+	return i
+}
+
+func (i Int128) Abs__1() (ret Int128, inRange bool) {
+	return i.Abs__0(), i.hi != 0x8000000000000000 || i.lo != 0
+}
+
+// AbsU returns the absolute value of i as an unsigned integer. All
+// values of i are representable using this function, but the type is
+// changed.
+func (i Int128) AbsU() Uint128 {
+	if i.hi == 0x8000000000000000 && i.lo == 0 {
+		return Uint128{hi: 0x8000000000000000}
+	}
+	if i.hi&signBit != 0 {
+		i.hi = ^i.hi
+		i.lo = ^(i.lo - 1)
+		if i.lo == 0 { // handle carry
+			i.hi++
+		}
+	}
+	return Uint128{hi: i.hi, lo: i.lo}
+}
+
+// Cmp compares i to n and returns:
+//
+//	< 0 if i <  n
+//	  0 if i == n
+//	> 0 if i >  n
+//
+// The specific value returned by Cmp is undefined, but it is guaranteed to
+// satisfy the above constraints.
+func (i Int128) Cmp__1(n Int128) int {
+	if i.hi == n.hi && i.lo == n.lo {
+		return 0
+	} else if i.hi&signBit == n.hi&signBit {
+		if i.hi > n.hi || (i.hi == n.hi && i.lo > n.lo) {
+			return 1
+		}
+	} else if i.hi&signBit == 0 {
+		return 1
+	}
+	return -1
+}
+
+// Cmp64 compares 'i' to 64-bit int 'n' and returns:
+//
+//	< 0 if i <  n
+//	  0 if i == n
+//	> 0 if i >  n
+//
+// The specific value returned by Cmp is undefined, but it is guaranteed to
+// satisfy the above constraints.
+func (i Int128) Cmp__0(n int64) int {
+	var nhi uint64
+	var nlo = uint64(n)
+	if n < 0 {
+		nhi = maxUint64
+	}
+	if i.hi == nhi && i.lo == nlo {
+		return 0
+	} else if i.hi&signBit == nhi&signBit {
+		if i.hi > nhi || (i.hi == nhi && i.lo > nlo) {
+			return 1
+		}
+	} else if i.hi&signBit == 0 {
+		return 1
+	}
+	return -1
+}
+
+func (i Int128) Gop_EQ__1(n Int128) bool {
+	return i.hi == n.hi && i.lo == n.lo
+}
+
+func (i Int128) Gop_EQ__0(n int64) bool {
+	var nhi uint64
+	var nlo = uint64(n)
+	if n < 0 {
+		nhi = maxUint64
+	}
+	return i.hi == nhi && i.lo == nlo
+}
+
+func (i Int128) Gop_GT__1(n Int128) bool {
+	if i.hi&signBit == n.hi&signBit {
+		return i.hi > n.hi || (i.hi == n.hi && i.lo > n.lo)
+	} else if i.hi&signBit == 0 {
+		return true
+	}
+	return false
+}
+
+func (i Int128) Gop_GT__0(n int64) bool {
+	var nhi uint64
+	var nlo = uint64(n)
+	if n < 0 {
+		nhi = maxUint64
+	}
+
+	if i.hi&signBit == nhi&signBit {
+		return i.hi > nhi || (i.hi == nhi && i.lo > nlo)
+	} else if i.hi&signBit == 0 {
+		return true
+	}
+	return false
+}
+
+func (i Int128) Gop_GE__1(n Int128) bool {
+	if i.hi == n.hi && i.lo == n.lo {
+		return true
+	}
+	if i.hi&signBit == n.hi&signBit {
+		return i.hi > n.hi || (i.hi == n.hi && i.lo > n.lo)
+	} else if i.hi&signBit == 0 {
+		return true
+	}
+	return false
+}
+
+func (i Int128) Gop_GE__0(n int64) bool {
+	var nhi uint64
+	var nlo = uint64(n)
+	if n < 0 {
+		nhi = maxUint64
+	}
+
+	if i.hi == nhi && i.lo == nlo {
+		return true
+	}
+	if i.hi&signBit == nhi&signBit {
+		return i.hi > nhi || (i.hi == nhi && i.lo > nlo)
+	} else if i.hi&signBit == 0 {
+		return true
+	}
+	return false
+}
+
+func (i Int128) Gop_LT__1(n Int128) bool {
+	if i.hi&signBit == n.hi&signBit {
+		return i.hi < n.hi || (i.hi == n.hi && i.lo < n.lo)
+	} else if i.hi&signBit != 0 {
+		return true
+	}
+	return false
+}
+
+func (i Int128) Gop_LT__0(n int64) bool {
+	var nhi uint64
+	var nlo = uint64(n)
+	if n < 0 {
+		nhi = maxUint64
+	}
+
+	if i.hi&signBit == nhi&signBit {
+		return i.hi < nhi || (i.hi == nhi && i.lo < nlo)
+	} else if i.hi&signBit != 0 {
+		return true
+	}
+	return false
+}
+
+func (i Int128) Gop_LE__1(n Int128) bool {
+	if i.hi == n.hi && i.lo == n.lo {
+		return true
+	}
+	if i.hi&signBit == n.hi&signBit {
+		return i.hi < n.hi || (i.hi == n.hi && i.lo < n.lo)
+	} else if i.hi&signBit != 0 {
+		return true
+	}
+	return false
+}
+
+func (i Int128) Gop_LE__0(n int64) bool {
+	var nhi uint64
+	var nlo = uint64(n)
+	if n < 0 {
+		nhi = maxUint64
+	}
+
+	if i.hi == nhi && i.lo == nlo {
+		return true
+	}
+	if i.hi&signBit == nhi&signBit {
+		return i.hi < nhi || (i.hi == nhi && i.lo < nlo)
+	} else if i.hi&signBit != 0 {
+		return true
+	}
+	return false
+}
+
+// Mul returns the product of two I128s.
+//
+// Overflow should wrap around, as per the Go spec.
+//
+func (i Int128) Gop_Mul__1(n Int128) (dest Int128) {
+	hi, lo := bits.Mul64(i.lo, n.lo)
+	hi += i.hi*n.lo + i.lo*n.hi
+	return Int128{hi, lo}
+}
+
+func (i Int128) Gop_Mul__0(n int64) Int128 {
+	nlo := uint64(n)
+	var nhi uint64
+	if n < 0 {
+		nhi = maxUint64
+	}
+	hi, lo := bits.Mul64(i.lo, nlo)
+	hi += i.hi*nlo + i.lo*nhi
+	return Int128{hi, lo}
+}
+
+// QuoRem returns the quotient q and remainder r for y != 0. If y == 0, a
+// division-by-zero run-time panic occurs.
+//
+// QuoRem implements T-division and modulus (like Go):
+//
+//	q = x/y      with the result truncated to zero
+//	r = x - y*q
+//
+// U128 does not support big.Int.DivMod()-style Euclidean division.
+//
+// Note: dividing MinI128 by -1 will overflow, returning MinI128, as
+// per the Go spec (https://golang.org/ref/spec#Integer_operators):
+//
+//	The one exception to this rule is that if the dividend x is the most
+//	negative value for the int type of x, the quotient q = x / -1 is equal to x
+//	(and r = 0) due to two's-complement integer overflow.
+func (i Int128) QuoRem__1(by Int128) (q, r Int128) {
+	qSign, rSign := 1, 1
+	if i.Gop_LT__0(0) {
+		qSign, rSign = -1, -1
+		i = i.Gop_Neg()
+	}
+	if by.Gop_LT__0(0) {
+		qSign = -qSign
+		by = by.Gop_Neg()
+	}
+
+	qu, ru := i.Gop_Rcast__0().QuoRem__1(by.Gop_Rcast__0())
+	q, r = Int128_Cast__7(qu), Int128_Cast__7(ru)
+	if qSign < 0 {
+		q = q.Gop_Neg()
+	}
+	if rSign < 0 {
+		r = r.Gop_Neg()
+	}
+	return q, r
+}
+
+func (i Int128) QuoRem__0(by int64) (q, r Int128) {
+	ineg := i.hi&signBit != 0
+	if ineg {
+		i = i.Gop_Neg()
+	}
+	byneg := by < 0
+	if byneg {
+		by = -by
+	}
+
+	n := uint64(by)
+	if i.hi < n {
+		q.lo, r.lo = bits.Div64(i.hi, i.lo, n)
+	} else {
+		q.hi, r.lo = bits.Div64(0, i.hi, n)
+		q.lo, r.lo = bits.Div64(r.lo, i.lo, n)
+	}
+	if ineg != byneg {
+		q = q.Gop_Neg()
+	}
+	if ineg {
+		r = r.Gop_Neg()
+	}
+	return q, r
+}
+
+// Quo returns the quotient x/y for y != 0. If y == 0, a division-by-zero
+// run-time panic occurs. Quo implements truncated division (like Go); see
+// QuoRem for more details.
+func (i Int128) Gop_Quo__1(by Int128) (q Int128) {
+	qSign := 1
+	if i.Gop_LT__0(0) {
+		qSign = -1
+		i = i.Gop_Neg()
+	}
+	if by.Gop_LT__0(0) {
+		qSign = -qSign
+		by = by.Gop_Neg()
+	}
+
+	qu := i.Gop_Rcast__0().Gop_Quo__1(by.Gop_Rcast__0())
+	q = Int128_Cast__7(qu)
+	if qSign < 0 {
+		q = q.Gop_Neg()
+	}
+	return q
+}
+
+func (i Int128) Gop_Quo__0(by int64) (q Int128) {
+	ineg := i.hi&signBit != 0
+	if ineg {
+		i = i.Gop_Neg()
+	}
+	byneg := by < 0
+	if byneg {
+		by = -by
+	}
+
+	n := uint64(by)
+	if i.hi < n {
+		q.lo, _ = bits.Div64(i.hi, i.lo, n)
+	} else {
+		var rlo uint64
+		q.hi, rlo = bits.Div64(0, i.hi, n)
+		q.lo, _ = bits.Div64(rlo, i.lo, n)
+	}
+	if ineg != byneg {
+		q = q.Gop_Neg()
+	}
+	return q
+}
+
+// Rem returns the remainder of x%y for y != 0. If y == 0, a division-by-zero
+// run-time panic occurs. Rem implements truncated modulus (like Go); see
+// QuoRem for more details.
+func (i Int128) Rem__1(by Int128) (r Int128) {
+	_, r = i.QuoRem__1(by)
+	return r
+}
+
+func (i Int128) Rem__0(by int64) (r Int128) {
+	ineg := i.hi&signBit != 0
+	if ineg {
+		i = i.Gop_Neg()
+	}
+	if by < 0 {
+		by = -by
+	}
+
+	n := uint64(by)
+	if i.hi < n {
+		_, r.lo = bits.Div64(i.hi, i.lo, n)
+	} else {
+		_, r.lo = bits.Div64(0, i.hi, n)
+		_, r.lo = bits.Div64(r.lo, i.lo, n)
+	}
+	if ineg {
+		r = r.Gop_Neg()
+	}
+	return r
+}
+
+// -----------------------------------------------------------------------------
+
+func ParseInt128(s string, base int) (out Int128, err error) {
+	b, ok := new(big.Int).SetString(s, base)
+	if !ok {
+		err = fmt.Errorf("invalid int128 string: %q", s)
+		return
+	}
+	out, inRange := Int128_Cast__9(b)
+	if !inRange {
+		err = fmt.Errorf("string %q was not in valid int128 range", s)
+	}
+	return
+}
+
+func FormatInt128(i Int128, base int) string {
+	return i.Text(base)
+}
+
+// -----------------------------------------------------------------------------
diff --git a/builtin/ng/uint128.go b/builtin/ng/uint128.go
new file mode 100644
index 00000000..f6d6a7d8
--- /dev/null
+++ b/builtin/ng/uint128.go
@@ -0,0 +1,915 @@
+package ng
+
+import (
+	"fmt"
+	"log"
+	"math/big"
+	"math/bits"
+	"strconv"
+)
+
+const (
+	GopPackage = true // to indicate this is a Go+ package
+)
+
+const (
+	maxUint64 = (1 << 64) - 1
+	intSize   = 32 << (^uint(0) >> 63)
+)
+
+//
+// Gop_: Go+ object prefix
+// Gop_xxx_Cast: type Gop_xxx typecast
+// xxxx__N: the Nth overload function
+//
+
+type Gop_ninteger = uint
+
+// -----------------------------------------------------------------------------
+
+type Uint128 struct {
+	hi, lo uint64
+}
+
+// Uint128_Init: func uint128.init(v int) uint128
+func Uint128_Init__0(v int) (out Uint128) {
+	if v < 0 {
+		panic("TODO: can't init uint128 from a negative integer")
+	}
+	return Uint128{lo: uint64(v)}
+}
+
+// Uint128_Init: func bigint.init(v untyped_bigint) bigint
+func Uint128_Init__1(v UntypedBigint) (out Uint128) {
+	out, inRange := Uint128_Cast__9(v)
+	if !inRange {
+		log.Panicf("value %v was not in valid uint128 range\n", v)
+	}
+	return
+}
+
+// Uint128_Cast: func uint128(v untyped_int) uint128
+func Uint128_Cast__0(v int) Uint128 {
+	return Uint128_Cast__6(int64(v))
+}
+
+// Uint128_Cast: func uint128(v untyped_bigint) uint128
+func Uint128_Cast__1(v UntypedBigint) Uint128 {
+	return Uint128_Init__1(v)
+}
+
+// Uint128_Cast: func uint128(v uint64) uint128
+func Uint128_Cast__2(v uint64) Uint128 {
+	return Uint128{lo: v}
+}
+
+// Uint128_Cast: func uint128(v uint32) uint128
+func Uint128_Cast__3(v uint32) Uint128 {
+	return Uint128{lo: uint64(v)}
+}
+
+// Uint128_Cast: func uint128(v uint16) uint128
+func Uint128_Cast__4(v uint16) Uint128 {
+	return Uint128{lo: uint64(v)}
+}
+
+// Uint128_Cast: func uint128(v uint8) uint128
+func Uint128_Cast__5(v uint8) Uint128 {
+	return Uint128{lo: uint64(v)}
+}
+
+// Uint128_Cast: func uint128(v int64) uint128
+func Uint128_Cast__6(v int64) (out Uint128) {
+	if v < 0 {
+		return Uint128{hi: maxUint64, lo: uint64(v)}
+	}
+	return Uint128{lo: uint64(v)}
+}
+
+// Uint128_Cast: func uint128(v int64) (uint128, bool)
+func Uint128_Cast__7(v int64) (out Uint128, inRange bool) {
+	if v < 0 {
+		return
+	}
+	return Uint128{lo: uint64(v)}, true
+}
+
+// Uint128_Cast: func uint128(v *big.Int) uint128
+func Uint128_Cast__8(v *big.Int) Uint128 {
+	out, _ := Uint128_Cast__9(v)
+	return out
+}
+
+// Uint128_Cast: func uint128(v *big.Int) (uint128, bool)
+func Uint128_Cast__9(v *big.Int) (out Uint128, inRange bool) {
+	if v.Sign() < 0 {
+		return out, false
+	}
+
+	words := v.Bits()
+	switch intSize {
+	case 64:
+		lw := len(words)
+		switch lw {
+		case 0:
+			return Uint128{}, true
+		case 1:
+			return Uint128{lo: uint64(words[0])}, true
+		case 2:
+			return Uint128{hi: uint64(words[1]), lo: uint64(words[0])}, true
+		default:
+			return Uint128{hi: maxUint64, lo: maxUint64}, false
+		}
+
+	case 32:
+		lw := len(words)
+		switch lw {
+		case 0:
+			return Uint128{}, true
+		case 1:
+			return Uint128{lo: uint64(words[0])}, true
+		case 2:
+			return Uint128{lo: (uint64(words[1]) << 32) | (uint64(words[0]))}, true
+		case 3:
+			return Uint128{hi: uint64(words[2]), lo: (uint64(words[1]) << 32) | (uint64(words[0]))}, true
+		case 4:
+			return Uint128{
+				hi: (uint64(words[3]) << 32) | (uint64(words[2])),
+				lo: (uint64(words[1]) << 32) | (uint64(words[0])),
+			}, true
+		default:
+			return Uint128{hi: maxUint64, lo: maxUint64}, false
+		}
+
+	default:
+		panic("unsupported bit size")
+	}
+}
+
+// Uint128_Cast: func uint128() uint128
+func Uint128_Cast__a() Uint128 {
+	return Uint128{}
+}
+
+// Uint128_Cast: func uint128(v uint) uint128
+func Uint128_Cast__b(v uint) Uint128 {
+	return Uint128{lo: uint64(v)}
+}
+
+// Uint128_Cast: func uint128(hi, lo uint64) uint128
+func Uint128_Cast__c(hi, lo uint64) Uint128 {
+	return Uint128{hi: hi, lo: lo}
+}
+
+// Gop_Rcast: func float64(v uint128) float64
+func (u Uint128) Gop_Rcast__0() float64 {
+	if u.hi == 0 {
+		return float64(u.lo)
+	}
+	return (float64(u.hi) * (1 << 64)) + float64(u.lo)
+}
+
+// Gop_Rcast: func uint64(v uint128) uint64
+func (u Uint128) Gop_Rcast__1() uint64 {
+	return u.lo
+}
+
+// Gop_Rcast: func uint64(v uint128) (uint64, bool)
+func (u Uint128) Gop_Rcast__2() (out uint64, inRange bool) {
+	return u.lo, u.hi == 0
+}
+
+// -----------------------------------------------------------------------------
+
+func (u Uint128) IsZero() bool {
+	return u.lo == 0 && u.hi == 0
+}
+
+func (u *Uint128) Scan(state fmt.ScanState, verb rune) (err error) {
+	t, err := state.Token(true, nil)
+	if err != nil {
+		return
+	}
+	v, err := ParseUint128(string(t), 10)
+	if err == nil {
+		*u = v
+	}
+	return
+}
+
+func (u Uint128) Format(s fmt.State, c rune) {
+	// TODO: not so good
+	u.BigInt().Format(s, c)
+}
+
+func (u Uint128) String() string {
+	return u.Text(10)
+}
+
+func (u Uint128) Text(base int) string {
+	if u.hi == 0 {
+		return strconv.FormatUint(u.lo, base)
+	}
+	// TODO: not so good
+	return u.BigInt().Text(base)
+}
+
+func (u Uint128) BigInt() *big.Int {
+	var v big.Int
+	u.ToBigInt(&v)
+	return &v
+}
+
+func (u Uint128) ToBigInt(b *big.Int) {
+	switch intSize {
+	case 64:
+		bits := b.Bits()
+		ln := len(bits)
+		if len(bits) < 2 {
+			bits = append(bits, make([]big.Word, 2-ln)...)
+		}
+		bits = bits[:2]
+		bits[0] = big.Word(u.lo)
+		bits[1] = big.Word(u.hi)
+		b.SetBits(bits)
+
+	case 32:
+		bits := b.Bits()
+		ln := len(bits)
+		if len(bits) < 4 {
+			bits = append(bits, make([]big.Word, 4-ln)...)
+		}
+		bits = bits[:4]
+		bits[0] = big.Word(u.lo & 0xFFFFFFFF)
+		bits[1] = big.Word(u.lo >> 32)
+		bits[2] = big.Word(u.hi & 0xFFFFFFFF)
+		bits[3] = big.Word(u.hi >> 32)
+		b.SetBits(bits)
+
+	default:
+		if u.hi > 0 {
+			b.SetUint64(u.hi)
+			b.Lsh(b, 64)
+		}
+		var lo big.Int
+		lo.SetUint64(u.lo)
+		b.Add(b, &lo)
+	}
+}
+
+// Bit returns the value of the i'th bit of x. That is, it returns (x>>i)&1.
+// The bit index i must be 0 <= i < 128
+func (u Uint128) Bit(i int) uint {
+	if i < 0 || i >= 128 {
+		panic("bit out of range")
+	}
+	if i >= 64 {
+		return uint((u.hi >> uint(i-64)) & 1)
+	} else {
+		return uint((u.lo >> uint(i)) & 1)
+	}
+}
+
+// SetBit returns a Uint128 with u's i'th bit set to b (0 or 1).
+// If b is not 0 or 1, SetBit will panic. If i < 0, SetBit will panic.
+func (u Uint128) SetBit(i int, b uint) (out Uint128) {
+	if i < 0 || i >= 128 {
+		panic("bit out of range")
+	}
+	if b == 0 {
+		if i >= 64 {
+			u.hi = u.hi &^ (1 << uint(i-64))
+		} else {
+			u.lo = u.lo &^ (1 << uint(i))
+		}
+	} else if b == 1 {
+		if i >= 64 {
+			u.hi = u.hi | (1 << uint(i-64))
+		} else {
+			u.lo = u.lo | (1 << uint(i))
+		}
+	} else {
+		panic("bit value not 0 or 1")
+	}
+	return u
+}
+
+func (u Uint128) LeadingZeros() int {
+	if u.hi == 0 {
+		return bits.LeadingZeros64(u.lo) + 64
+	} else {
+		return bits.LeadingZeros64(u.hi)
+	}
+}
+
+func (u Uint128) TrailingZeros() int {
+	if u.lo == 0 {
+		return bits.TrailingZeros64(u.hi) + 64
+	} else {
+		return bits.TrailingZeros64(u.lo)
+	}
+}
+
+// BitLen returns the length of the absolute value of u in bits. The bit length of 0 is 0.
+func (u Uint128) BitLen() int {
+	if u.hi > 0 {
+		return bits.Len64(u.hi) + 64
+	}
+	return bits.Len64(u.lo)
+}
+
+// OnesCount returns the number of one bits ("population count") in u.
+func (u Uint128) OnesCount() int {
+	return bits.OnesCount64(u.hi) + bits.OnesCount64(u.lo)
+}
+
+func (u Uint128) Reverse() Uint128 {
+	return Uint128{hi: bits.Reverse64(u.lo), lo: bits.Reverse64(u.hi)}
+}
+
+func (u Uint128) ReverseBytes() Uint128 {
+	return Uint128{hi: bits.ReverseBytes64(u.lo), lo: bits.ReverseBytes64(u.hi)}
+}
+
+// Cmp compares 'u' to 'n' and returns:
+//
+//	< 0 if u <  n
+//	  0 if u == n
+//	> 0 if u >  n
+//
+// The specific value returned by Cmp is undefined, but it is guaranteed to
+// satisfy the above constraints.
+//
+func (u Uint128) Cmp__1(n Uint128) int {
+	if u.hi == n.hi {
+		if u.lo > n.lo {
+			return 1
+		} else if u.lo < n.lo {
+			return -1
+		}
+	} else {
+		if u.hi > n.hi {
+			return 1
+		} else if u.hi < n.hi {
+			return -1
+		}
+	}
+	return 0
+}
+
+func (u Uint128) Cmp__0(n uint64) int {
+	if u.hi > 0 || u.lo > n {
+		return 1
+	} else if u.lo < n {
+		return -1
+	}
+	return 0
+}
+
+func (u Uint128) Gop_Inc() (v Uint128) {
+	var carry uint64
+	v.lo, carry = bits.Add64(u.lo, 1, 0)
+	v.hi = u.hi + carry
+	return v
+}
+
+func (u Uint128) Gop_Dec() (v Uint128) {
+	var borrowed uint64
+	v.lo, borrowed = bits.Sub64(u.lo, 1, 0)
+	v.hi = u.hi - borrowed
+	return v
+}
+
+func (u Uint128) Gop_Add__1(n Uint128) (v Uint128) {
+	var carry uint64
+	v.lo, carry = bits.Add64(u.lo, n.lo, 0)
+	v.hi, _ = bits.Add64(u.hi, n.hi, carry)
+	return v
+}
+
+func (u Uint128) Gop_Add__0(n uint64) (v Uint128) {
+	var carry uint64
+	v.lo, carry = bits.Add64(u.lo, n, 0)
+	v.hi = u.hi + carry
+	return v
+}
+
+func (u Uint128) Gop_Sub__1(n Uint128) (v Uint128) {
+	var borrowed uint64
+	v.lo, borrowed = bits.Sub64(u.lo, n.lo, 0)
+	v.hi, _ = bits.Sub64(u.hi, n.hi, borrowed)
+	return v
+}
+
+func (u Uint128) Gop_Sub__0(n uint64) (v Uint128) {
+	var borrowed uint64
+	v.lo, borrowed = bits.Sub64(u.lo, n, 0)
+	v.hi = u.hi - borrowed
+	return v
+}
+
+func (u Uint128) Gop_EQ__1(n Uint128) bool {
+	return u.hi == n.hi && u.lo == n.lo
+}
+
+func (u Uint128) Gop_EQ__0(n uint64) bool {
+	return u.hi == 0 && u.lo == n
+}
+
+func (u Uint128) Gop_GT__1(n Uint128) bool {
+	return u.hi > n.hi || (u.hi == n.hi && u.lo > n.lo)
+}
+
+func (u Uint128) Gop_GT__0(n uint64) bool {
+	return u.hi > 0 || u.lo > n
+}
+
+func (u Uint128) Gop_GE__1(n Uint128) bool {
+	return u.hi > n.hi || (u.hi == n.hi && u.lo >= n.lo)
+}
+
+func (u Uint128) Gop_GE__0(n uint64) bool {
+	return u.hi > 0 || u.lo >= n
+}
+
+func (u Uint128) Gop_LT__1(n Uint128) bool {
+	return u.hi < n.hi || (u.hi == n.hi && u.lo < n.lo)
+}
+
+func (u Uint128) Gop_LT__0(n uint64) bool {
+	return u.hi == 0 && u.lo < n
+}
+
+func (u Uint128) Gop_LE__1(n Uint128) bool {
+	return u.hi < n.hi || (u.hi == n.hi && u.lo <= n.lo)
+}
+
+func (u Uint128) Gop_LE__0(n uint64) bool {
+	return u.hi == 0 && u.lo <= n
+}
+
+func (u Uint128) Gop_And__1(n Uint128) Uint128 {
+	u.hi = u.hi & n.hi
+	u.lo = u.lo & n.lo
+	return u
+}
+
+func (u Uint128) Gop_And__0(n uint64) Uint128 {
+	return Uint128{lo: u.lo & n}
+}
+
+func (u Uint128) Gop_AndNot(n Uint128) Uint128 {
+	u.hi = u.hi &^ n.hi
+	u.lo = u.lo &^ n.lo
+	return u
+}
+
+func (u Uint128) Gop_Not() (out Uint128) {
+	out.hi = ^u.hi
+	out.lo = ^u.lo
+	return out
+}
+
+func (u Uint128) Gop_Or__1(n Uint128) (out Uint128) {
+	out.hi = u.hi | n.hi
+	out.lo = u.lo | n.lo
+	return out
+}
+
+func (u Uint128) Gop_Or__0(n uint64) Uint128 {
+	u.lo = u.lo | n
+	return u
+}
+
+func (u Uint128) Gop_Xor__1(v Uint128) Uint128 {
+	u.hi = u.hi ^ v.hi
+	u.lo = u.lo ^ v.lo
+	return u
+}
+
+func (u Uint128) Gop_Xor__0(v uint64) Uint128 {
+	u.hi = u.hi ^ 0
+	u.lo = u.lo ^ v
+	return u
+}
+
+func (u Uint128) Gop_Lsh(n Gop_ninteger) (v Uint128) {
+	if n == 0 {
+		return u
+	} else if n > 64 {
+		v.hi = u.lo << (n - 64)
+		v.lo = 0
+	} else if n < 64 {
+		v.hi = (u.hi << n) | (u.lo >> (64 - n))
+		v.lo = u.lo << n
+	} else if n == 64 {
+		v.hi = u.lo
+		v.lo = 0
+	}
+	return v
+}
+
+func (u Uint128) Gop_Rsh(n Gop_ninteger) (v Uint128) {
+	if n == 0 {
+		return u
+	} else if n > 64 {
+		v.lo = u.hi >> (n - 64)
+		v.hi = 0
+	} else if n < 64 {
+		v.lo = (u.lo >> n) | (u.hi << (64 - n))
+		v.hi = u.hi >> n
+	} else if n == 64 {
+		v.lo = u.hi
+		v.hi = 0
+	}
+
+	return v
+}
+
+func (u Uint128) Gop_Mul__1(n Uint128) Uint128 {
+	hi, lo := bits.Mul64(u.lo, n.lo)
+	hi += u.hi*n.lo + u.lo*n.hi
+	return Uint128{hi, lo}
+}
+
+func (u Uint128) Gop_Mul__0(n uint64) (dest Uint128) {
+	dest.hi, dest.lo = bits.Mul64(u.lo, n)
+	dest.hi += u.hi * n
+	return dest
+}
+
+const (
+	divAlgoLeading0Spill = 16
+)
+
+func (u Uint128) Gop_Quo__1(by Uint128) (q Uint128) {
+	if by.lo == 0 && by.hi == 0 {
+		panic("division by zero")
+	}
+
+	if u.hi|by.hi == 0 {
+		q.lo = u.lo / by.lo // FIXME: div/0 risk?
+		return q
+	}
+
+	var byLoLeading0, byHiLeading0, byLeading0 uint
+	if by.hi == 0 {
+		byLoLeading0, byHiLeading0 = uint(bits.LeadingZeros64(by.lo)), 64
+		byLeading0 = byLoLeading0 + 64
+	} else {
+		byHiLeading0 = uint(bits.LeadingZeros64(by.hi))
+		byLeading0 = byHiLeading0
+	}
+
+	if byLeading0 == 127 {
+		return u
+	}
+
+	byTrailing0 := uint(by.TrailingZeros())
+	if (byLeading0 + byTrailing0) == 127 {
+		return u.Gop_Rsh(byTrailing0)
+	}
+
+	if cmp := u.Cmp__1(by); cmp < 0 {
+		return q // it's 100% remainder
+	} else if cmp == 0 {
+		q.lo = 1 // dividend and divisor are the same
+		return q
+	}
+
+	uLeading0 := uint(u.LeadingZeros())
+	if byLeading0-uLeading0 > divAlgoLeading0Spill {
+		q, _ = quorem128by128(u, by, byHiLeading0, byLoLeading0)
+		return q
+	} else {
+		return quo128bin(u, by, uLeading0, byLeading0)
+	}
+}
+
+func (u Uint128) Gop_Quo__0(by uint64) (q Uint128) {
+	if u.hi < by {
+		q.lo, _ = bits.Div64(u.hi, u.lo, by)
+	} else {
+		q.hi = u.hi / by
+		q.lo, _ = bits.Div64(u.hi%by, u.lo, by)
+	}
+	return q
+}
+
+func (u Uint128) QuoRem__1(by Uint128) (q, r Uint128) {
+	if by.lo == 0 && by.hi == 0 {
+		panic("division by zero")
+	}
+
+	if u.hi|by.hi == 0 {
+		q.lo = u.lo / by.lo
+		r.lo = u.lo % by.lo
+		return q, r
+	}
+
+	var byLoLeading0, byHiLeading0, byLeading0 uint
+	if by.hi == 0 {
+		byLoLeading0, byHiLeading0 = uint(bits.LeadingZeros64(by.lo)), 64
+		byLeading0 = byLoLeading0 + 64
+	} else {
+		byHiLeading0 = uint(bits.LeadingZeros64(by.hi))
+		byLeading0 = byHiLeading0
+	}
+
+	if byLeading0 == 127 {
+		return u, r
+	}
+
+	byTrailing0 := uint(by.TrailingZeros())
+	if (byLeading0 + byTrailing0) == 127 {
+		q = u.Gop_Rsh(byTrailing0)
+		by = by.Gop_Dec()
+		r = by.Gop_And__1(u)
+		return
+	}
+
+	if cmp := u.Cmp__1(by); cmp < 0 {
+		return q, u // it's 100% remainder
+
+	} else if cmp == 0 {
+		q.lo = 1 // dividend and divisor are the same
+		return q, r
+	}
+
+	uLeading0 := uint(u.LeadingZeros())
+	if byLeading0-uLeading0 > divAlgoLeading0Spill {
+		return quorem128by128(u, by, byHiLeading0, byLoLeading0)
+	} else {
+		return quorem128bin(u, by, uLeading0, byLeading0)
+	}
+}
+
+func (u Uint128) QuoRem__0(by uint64) (q, r Uint128) {
+	if u.hi < by {
+		q.lo, r.lo = bits.Div64(u.hi, u.lo, by)
+	} else {
+		q.hi, r.lo = bits.Div64(0, u.hi, by)
+		q.lo, r.lo = bits.Div64(r.lo, u.lo, by)
+	}
+	return q, r
+}
+
+// Gop_Rem: func (a uint128) % (b uint128) uint128
+func (u Uint128) Gop_Rem__1(by Uint128) (r Uint128) {
+	// TODO: inline only the needed bits
+	_, r = u.QuoRem__1(by)
+	return r
+}
+
+func (u Uint128) Gop_Rem__0(by uint64) (r Uint128) {
+	// https://github.com/golang/go/issues/28970
+	// if u.hi < by {
+	//     _, r.lo = bits.Rem64(u.hi, u.lo, by)
+	// } else {
+	//     _, r.lo = bits.Rem64(bits.Rem64(0, u.hi, by), u.lo, by)
+	// }
+
+	if u.hi < by {
+		_, r.lo = bits.Div64(u.hi, u.lo, by)
+	} else {
+		_, r.lo = bits.Div64(0, u.hi, by)
+		_, r.lo = bits.Div64(r.lo, u.lo, by)
+	}
+	return r
+}
+
+// Hacker's delight 9-4, divlu:
+func quo128by64(u1, u0, v uint64, vLeading0 uint) (q uint64) {
+	var b uint64 = 1 << 32
+	var un1, un0, vn1, vn0, q1, q0, un32, un21, un10, rhat, vs, left, right uint64
+
+	vs = v << vLeading0
+
+	vn1 = vs >> 32
+	vn0 = vs & 0xffffffff
+
+	if vLeading0 > 0 {
+		un32 = (u1 << vLeading0) | (u0 >> (64 - vLeading0))
+		un10 = u0 << vLeading0
+	} else {
+		un32 = u1
+		un10 = u0
+	}
+
+	un1 = un10 >> 32
+	un0 = un10 & 0xffffffff
+
+	q1 = un32 / vn1
+	rhat = un32 % vn1
+
+	left = q1 * vn0
+	right = (rhat << 32) | un1
+
+again1:
+	if (q1 >= b) || (left > right) {
+		q1--
+		rhat += vn1
+		if rhat < b {
+			left -= vn0
+			right = (rhat << 32) | un1
+			goto again1
+		}
+	}
+
+	un21 = (un32 << 32) + (un1 - (q1 * vs))
+
+	q0 = un21 / vn1
+	rhat = un21 % vn1
+
+	left = q0 * vn0
+	right = (rhat << 32) | un0
+
+again2:
+	if (q0 >= b) || (left > right) {
+		q0--
+		rhat += vn1
+		if rhat < b {
+			left -= vn0
+			right = (rhat << 32) | un0
+			goto again2
+		}
+	}
+
+	return (q1 << 32) | q0
+}
+
+// Hacker's delight 9-4, divlu:
+func quorem128by64(u1, u0, v uint64, vLeading0 uint) (q, r uint64) {
+	var b uint64 = 1 << 32
+	var un1, un0, vn1, vn0, q1, q0, un32, un21, un10, rhat, left, right uint64
+
+	v <<= vLeading0
+
+	vn1 = v >> 32
+	vn0 = v & 0xffffffff
+
+	if vLeading0 > 0 {
+		un32 = (u1 << vLeading0) | (u0 >> (64 - vLeading0))
+		un10 = u0 << vLeading0
+	} else {
+		un32 = u1
+		un10 = u0
+	}
+
+	un1 = un10 >> 32
+	un0 = un10 & 0xffffffff
+
+	q1 = un32 / vn1
+	rhat = un32 % vn1
+
+	left = q1 * vn0
+	right = (rhat << 32) + un1
+
+again1:
+	if (q1 >= b) || (left > right) {
+		q1--
+		rhat += vn1
+		if rhat < b {
+			left -= vn0
+			right = (rhat << 32) | un1
+			goto again1
+		}
+	}
+
+	un21 = (un32 << 32) + (un1 - (q1 * v))
+
+	q0 = un21 / vn1
+	rhat = un21 % vn1
+
+	left = q0 * vn0
+	right = (rhat << 32) | un0
+
+again2:
+	if (q0 >= b) || (left > right) {
+		q0--
+		rhat += vn1
+		if rhat < b {
+			left -= vn0
+			right = (rhat << 32) | un0
+			goto again2
+		}
+	}
+
+	return (q1 << 32) | q0, ((un21 << 32) + (un0 - (q0 * v))) >> vLeading0
+}
+
+func quorem128by128(m, v Uint128, vHiLeading0, vLoLeading0 uint) (q, r Uint128) {
+	if v.hi == 0 {
+		if m.hi < v.lo {
+			q.lo, r.lo = quorem128by64(m.hi, m.lo, v.lo, vLoLeading0)
+			return q, r
+
+		} else {
+			q.hi = m.hi / v.lo
+			r.hi = m.hi % v.lo
+			q.lo, r.lo = quorem128by64(r.hi, m.lo, v.lo, vLoLeading0)
+			r.hi = 0
+			return q, r
+		}
+
+	} else {
+		v1 := v.Gop_Lsh(vHiLeading0)
+		u1 := m.Gop_Rsh(1)
+
+		var q1 Uint128
+		q1.lo = quo128by64(u1.hi, u1.lo, v1.hi, vLoLeading0)
+		q1 = q1.Gop_Rsh(63 - vHiLeading0)
+
+		if q1.hi|q1.lo != 0 {
+			q1 = q1.Gop_Dec()
+		}
+		q = q1
+		q1 = q1.Gop_Mul__1(v)
+		r = m.Gop_Sub__1(q1)
+
+		if r.Cmp__1(v) >= 0 {
+			q = q.Gop_Inc()
+			r = r.Gop_Sub__1(v)
+		}
+
+		return q, r
+	}
+}
+
+func quorem128bin(u, by Uint128, uLeading0, byLeading0 uint) (q, r Uint128) {
+	shift := int(byLeading0 - uLeading0)
+	by = by.Gop_Lsh(uint(shift))
+
+	for {
+		// q << 1
+		q.hi = (q.hi << 1) | (q.lo >> 63)
+		q.lo = q.lo << 1
+
+		// performance tweak: simulate greater than or equal by hand-inlining "not less than".
+		if u.hi > by.hi || (u.hi == by.hi && u.lo >= by.lo) {
+			u = u.Gop_Sub__1(by)
+			q.lo |= 1
+		}
+
+		// by >> 1
+		by.lo = (by.lo >> 1) | (by.hi << 63)
+		by.hi = by.hi >> 1
+
+		if shift <= 0 {
+			break
+		}
+		shift--
+	}
+
+	r = u
+	return q, r
+}
+
+func quo128bin(u, by Uint128, uLeading0, byLeading0 uint) (q Uint128) {
+	shift := int(byLeading0 - uLeading0)
+	by = by.Gop_Lsh(uint(shift))
+
+	for {
+		// q << 1
+		q.hi = (q.hi << 1) | (q.lo >> 63)
+		q.lo = q.lo << 1
+
+		// u >= by
+		if u.hi > by.hi || (u.hi == by.hi && u.lo >= by.lo) {
+			u = u.Gop_Sub__1(by)
+			q.lo |= 1
+		}
+
+		// q >> 1
+		by.lo = (by.lo >> 1) | (by.hi << 63)
+		by.hi = by.hi >> 1
+
+		if shift <= 0 {
+			break
+		}
+		shift--
+	}
+
+	return q
+}
+
+// -----------------------------------------------------------------------------
+
+func ParseUint128(s string, base int) (out Uint128, err error) {
+	b, ok := new(big.Int).SetString(s, base)
+	if !ok {
+		err = fmt.Errorf("invalid uint128 string: %q", s)
+		return
+	}
+	out, inRange := Uint128_Cast__9(b)
+	if !inRange {
+		err = fmt.Errorf("string %q was not in valid uint128 range", s)
+	}
+	return
+}
+
+func FormatUint128(i Uint128, base int) string {
+	return i.Text(base)
+}
+
+// -----------------------------------------------------------------------------
diff --git a/builtin/range.go b/builtin/range.go
index 5bd98c60..259104a1 100644
--- a/builtin/range.go
+++ b/builtin/range.go
@@ -16,6 +16,10 @@
 
 package builtin
 
+const (
+	GopPackage = true // to indicate this is a Go+ package
+)
+
 // -----------------------------------------------------------------------------
 
 type IntRange struct {
diff --git a/cl/_packages.go b/cl/_packages.go
deleted file mode 100644
index a4dbd82b..00000000
--- a/cl/_packages.go
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2021 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 cl
-
-import (
-	"io/ioutil"
-	"path/filepath"
-	"strings"
-	"syscall"
-
-	"github.com/goplus/gop/env"
-	"github.com/goplus/gop/x/mod/modfile"
-	"golang.org/x/tools/go/packages"
-)
-
-// -----------------------------------------------------------------------------
-
-func GetModulePath(file string) (pkgPath string, err error) {
-	src, err := ioutil.ReadFile(file)
-	if err != nil {
-		return
-	}
-	f, err := modfile.ParseLax(file, src, nil)
-	if err != nil {
-		return
-	}
-	return f.Module.Mod.Path, nil
-}
-
-// -----------------------------------------------------------------------------
-
-type PkgsLoader struct {
-	genGoPkg   func(pkgDir string, base *Config) error
-	BaseConfig *Config
-}
-
-func initPkgsLoader(base *Config) {
-	p := &PkgsLoader{genGoPkg: base.GenGoPkg, BaseConfig: base}
-	base.PkgsLoader = p
-}
-
-func (p *PkgsLoader) GenGoPkgs(cfg *packages.Config, notFounds []string) (err error) {
-	if p.genGoPkg == nil {
-		return syscall.ENOENT
-	}
-	modfile, err := env.GOPMOD(cfg.Dir)
-	if err != nil {
-		return
-	}
-	pkgPath, err := GetModulePath(modfile)
-	if err != nil {
-		return
-	}
-	root, _ := filepath.Split(modfile)
-	pkgPathSlash := pkgPath + "/"
-	for _, notFound := range notFounds {
-		if strings.HasPrefix(notFound, pkgPathSlash) || notFound == pkgPath {
-			if err = p.genGoPkg(root+notFound[len(pkgPath):], p.BaseConfig); err != nil {
-				return
-			}
-		} else {
-			return syscall.ENOENT
-		}
-	}
-	return nil
-}
-
-// -----------------------------------------------------------------------------
diff --git a/cl/builtin.go b/cl/builtin.go
index 35cdb1b3..0a5d5f15 100644
--- a/cl/builtin.go
+++ b/cl/builtin.go
@@ -27,16 +27,17 @@ import (
 
 func initMathBig(pkg gox.PkgImporter, conf *gox.Config, big *gox.PkgRef) {
 	big.EnsureImported()
-	conf.UntypedBigInt = big.Ref("Gop_untyped_bigint").Type().(*types.Named)
-	conf.UntypedBigRat = big.Ref("Gop_untyped_bigrat").Type().(*types.Named)
-	conf.UntypedBigFloat = big.Ref("Gop_untyped_bigfloat").Type().(*types.Named)
+	conf.UntypedBigInt = big.Ref("UntypedBigint").Type().(*types.Named)
+	conf.UntypedBigRat = big.Ref("UntypedBigrat").Type().(*types.Named)
+	conf.UntypedBigFloat = big.Ref("UntypedBigfloat").Type().(*types.Named)
 }
 
-func initBuiltin(pkg gox.PkgImporter, builtin *types.Package, fmt, big *gox.PkgRef) {
+func initBuiltin(pkg gox.PkgImporter, builtin *types.Package, fmt, ng, buil *gox.PkgRef) {
 	scope := builtin.Scope()
 	typs := []string{"bigint", "bigrat", "bigfloat"}
 	for _, typ := range typs {
-		scope.Insert(types.NewTypeName(token.NoPos, builtin, typ, big.Ref("Gop_"+typ).Type()))
+		name := string(typ[0]-('a'-'A')) + typ[1:]
+		scope.Insert(types.NewTypeName(token.NoPos, builtin, typ, ng.Ref(name).Type()))
 	}
 	fns := []string{
 		"print", "println", "printf", "errorf",
@@ -47,17 +48,21 @@ func initBuiltin(pkg gox.PkgImporter, builtin *types.Package, fmt, big *gox.PkgR
 		fnTitle := string(fn[0]-'a'+'A') + fn[1:]
 		scope.Insert(gox.NewOverloadFunc(token.NoPos, builtin, fn, fmt.Ref(fnTitle)))
 	}
-	scope.Insert(gox.NewOverloadFunc(token.NoPos, builtin, "newRange", big.Ref("NewRange__0")))
+	scope.Insert(gox.NewOverloadFunc(token.NoPos, builtin, "newRange", buil.Ref("NewRange__0")))
+	scope.Insert(types.NewTypeName(token.NoPos, builtin, "uint128", ng.Ref("Uint128").Type()))
+	scope.Insert(types.NewTypeName(token.NoPos, builtin, "int128", ng.Ref("Int128").Type()))
+	scope.Insert(types.NewTypeName(token.NoPos, builtin, "any", gox.TyEmptyInterface))
 }
 
 func newBuiltinDefault(pkg gox.PkgImporter, conf *gox.Config) *types.Package {
 	builtin := types.NewPackage("", "")
 	fmt := pkg.Import("fmt")
-	big := pkg.Import("github.com/goplus/gop/builtin")
+	buil := pkg.Import("github.com/goplus/gop/builtin")
+	ng := pkg.Import("github.com/goplus/gop/builtin/ng")
 	pkg.Import("strconv")
 	pkg.Import("strings")
-	initMathBig(pkg, conf, big)
-	initBuiltin(pkg, builtin, fmt, big)
+	initMathBig(pkg, conf, ng)
+	initBuiltin(pkg, builtin, fmt, ng, buil)
 	gox.InitBuiltin(pkg, builtin, conf)
 	return builtin
 }
diff --git a/cl/compile.go b/cl/compile.go
index 9cddd01c..4c0e2217 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -96,6 +96,9 @@ type Config struct {
 
 	// RelativePath = true means to generate file line comments with relative file path.
 	RelativePath bool
+
+	// NoAutoGenMain = true means not to auto generate main func is no entry.
+	NoAutoGenMain bool
 }
 
 type nodeInterp struct {
@@ -248,7 +251,7 @@ type blockCtx struct {
 	imports      map[string]*gox.PkgRef
 	lookups      []*gox.PkgRef
 	targetDir    string
-	classRecv    *ast.FieldList // avaliable when gmxSettings != nil
+	classRecv    *ast.FieldList // available when gmxSettings != nil
 	fileLine     bool
 	relativePath bool
 	isClass      bool
@@ -396,6 +399,15 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package,
 		load()
 	}
 	err = ctx.complete()
+
+	if !conf.NoAutoGenMain && pkg.Name == "main" {
+		if obj := p.Types.Scope().Lookup("main"); obj == nil {
+			old := p.SetInTestingFile(false)
+			p.NewFunc(nil, "main", nil, nil, false).BodyStart(p).End()
+			p.SetInTestingFile(old)
+		}
+	}
+
 	return
 }
 
@@ -542,6 +554,26 @@ func preloadFile(p *gox.Package, parent *pkgCtx, file string, f *ast.File, targe
 			},
 		}}}
 	}
+	// check class project no MainEntry and auto added
+	if !conf.NoAutoGenMain && f.IsProj && !f.NoEntrypoint && f.Name.Name == "main" {
+		entry := getEntrypoint(f, false)
+		var hasEntry bool
+		for _, decl := range f.Decls {
+			switch d := decl.(type) {
+			case *ast.FuncDecl:
+				if d.Name.Name == entry {
+					hasEntry = true
+				}
+			}
+		}
+		if !hasEntry {
+			f.Decls = append(f.Decls, &ast.FuncDecl{
+				Name: ast.NewIdent(entry),
+				Type: &ast.FuncType{Params: &ast.FieldList{}},
+				Body: &ast.BlockStmt{},
+			})
+		}
+	}
 	for _, decl := range f.Decls {
 		switch d := decl.(type) {
 		case *ast.FuncDecl:
diff --git a/cl/expr.go b/cl/expr.go
index 07e58477..1945aa2a 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -479,7 +479,7 @@ func compileLambda(ctx *blockCtx, lambda ast.Expr, sig *types.Signature) {
 	}
 }
 
-func compileLambdaParams(ctx *blockCtx, pos token.Pos, lhs []*ast.Ident, in *types.Tuple) []*types.Var {
+func makeLambdaParams(ctx *blockCtx, pos token.Pos, lhs []*ast.Ident, in *types.Tuple) *types.Tuple {
 	pkg := ctx.pkg
 	n := len(lhs)
 	if nin := in.Len(); n != nin {
@@ -494,53 +494,57 @@ func compileLambdaParams(ctx *blockCtx, pos token.Pos, lhs []*ast.Ident, in *typ
 		panic(ctx.newCodeErrorf(
 			pos, "too %s arguments in lambda expression\n\thave (%s)\n\twant %v", fewOrMany, strings.Join(has, ", "), in))
 	}
+	if n == 0 {
+		return nil
+	}
 	params := make([]*types.Var, n)
 	for i, name := range lhs {
 		params[i] = pkg.NewParam(name.Pos(), name.Name, in.At(i).Type())
 	}
-	return params
+	return types.NewTuple(params...)
 }
 
-func compileLambdaExpr(ctx *blockCtx, v *ast.LambdaExpr, sig *types.Signature) {
-	pkg := ctx.pkg
-	params := compileLambdaParams(ctx, v.Pos(), v.Lhs, sig.Params())
-	out := sig.Results()
+func makeLambdaResults(pkg *gox.Package, out *types.Tuple) *types.Tuple {
 	nout := out.Len()
+	if nout == 0 {
+		return nil
+	}
 	results := make([]*types.Var, nout)
 	for i := 0; i < nout; i++ {
 		results[i] = pkg.NewParam(token.NoPos, "", out.At(i).Type())
 	}
-	ctx.cb.NewClosure(types.NewTuple(params...), types.NewTuple(results...), false).BodyStart(pkg)
+	return types.NewTuple(results...)
+}
+
+func compileLambdaExpr(ctx *blockCtx, v *ast.LambdaExpr, sig *types.Signature) {
+	pkg := ctx.pkg
+	params := makeLambdaParams(ctx, v.Pos(), v.Lhs, sig.Params())
+	results := makeLambdaResults(pkg, sig.Results())
+	ctx.cb.NewClosure(params, results, false).BodyStart(pkg)
 	for _, v := range v.Rhs {
 		compileExpr(ctx, v)
 	}
-	ctx.cb.Return(nout).End()
+	ctx.cb.Return(len(v.Rhs)).End()
 }
 
 func compileLambdaExpr2(ctx *blockCtx, v *ast.LambdaExpr2, sig *types.Signature) {
 	pkg := ctx.pkg
-	params := compileLambdaParams(ctx, v.Pos(), v.Lhs, sig.Params())
-	cb := ctx.cb
-	comments := cb.Comments()
-	out := sig.Results()
-	nout := out.Len()
-	results := make([]*types.Var, nout)
-	for i := 0; i < nout; i++ {
-		results[i] = pkg.NewParam(token.NoPos, "", out.At(i).Type())
-	}
-	fn := cb.NewClosure(types.NewTuple(params...), types.NewTuple(results...), false)
+	params := makeLambdaParams(ctx, v.Pos(), v.Lhs, sig.Params())
+	results := makeLambdaResults(pkg, sig.Results())
+	comments, once := ctx.cb.BackupComments()
+	fn := ctx.cb.NewClosure(params, results, false)
 	loadFuncBody(ctx, fn, v.Body)
-	cb.SetComments(comments, false)
+	ctx.cb.SetComments(comments, once)
 }
 
 func compileFuncLit(ctx *blockCtx, v *ast.FuncLit) {
 	cb := ctx.cb
-	comments := cb.Comments()
+	comments, once := cb.BackupComments()
 	sig := toFuncType(ctx, v.Type, nil)
 	fn := cb.NewClosureWith(sig)
 	if body := v.Body; body != nil {
 		loadFuncBody(ctx, fn, body)
-		cb.SetComments(comments, false)
+		cb.SetComments(comments, once)
 	}
 }
 
diff --git a/cl/func_type_and_var.go b/cl/func_type_and_var.go
index f24de2da..632fadd0 100644
--- a/cl/func_type_and_var.go
+++ b/cl/func_type_and_var.go
@@ -209,15 +209,36 @@ func toStructType(ctx *blockCtx, v *ast.StructType) *types.Struct {
 	fieldList := v.Fields.List
 	fields := make([]*types.Var, 0, len(fieldList))
 	tags := make([]string, 0, len(fieldList))
+	names := make(map[string]token.Pos)
+	chkRedecl := func(name string, pos token.Pos) bool {
+		if name == "_" {
+			return false
+		}
+		if opos, ok := names[name]; ok {
+			npos := ctx.Position(pos)
+			ctx.handleCodeErrorf(&npos, "%v redeclared\n\t%v other declaration of %v",
+				name, ctx.Position(opos), name)
+			return true
+		}
+		names[name] = pos
+		return false
+	}
 	for _, field := range fieldList {
 		typ := toType(ctx, field.Type)
 		if field.Names == nil { // embedded
-			fld := types.NewField(token.NoPos, pkg, getTypeName(typ), typ, true)
+			name := getTypeName(typ)
+			if chkRedecl(name, field.Type.Pos()) {
+				continue
+			}
+			fld := types.NewField(token.NoPos, pkg, name, typ, true)
 			fields = append(fields, fld)
 			tags = append(tags, toFieldTag(field.Tag))
 			continue
 		}
 		for _, name := range field.Names {
+			if chkRedecl(name.Name, name.NamePos) {
+				continue
+			}
 			fld := types.NewField(token.NoPos, pkg, name.Name, typ, false)
 			fields = append(fields, fld)
 			tags = append(tags, toFieldTag(field.Tag))
diff --git a/cl/stmt.go b/cl/stmt.go
index 02b66f76..f83a339e 100644
--- a/cl/stmt.go
+++ b/cl/stmt.go
@@ -251,7 +251,7 @@ func compileRangeStmt(ctx *blockCtx, v *ast.RangeStmt) {
 		return
 	}
 	cb := ctx.cb
-	comments := cb.Comments()
+	comments, once := cb.BackupComments()
 	if v.Tok == token.DEFINE {
 		names := make([]string, 1, 2)
 		if v.Key == nil {
@@ -288,7 +288,7 @@ func compileRangeStmt(ctx *blockCtx, v *ast.RangeStmt) {
 	}
 	cb.RangeAssignThen(pos)
 	compileStmts(ctx, v.Body.List)
-	cb.SetComments(comments, true)
+	cb.SetComments(comments, once)
 	setBodyHandler(ctx)
 	cb.End()
 }
@@ -299,7 +299,7 @@ func compileForPhraseStmt(ctx *blockCtx, v *ast.ForPhraseStmt) {
 		return
 	}
 	cb := ctx.cb
-	comments := cb.Comments()
+	comments, once := cb.BackupComments()
 	names := make([]string, 1, 2)
 	if v.Key == nil {
 		names[0] = "_"
@@ -317,12 +317,12 @@ func compileForPhraseStmt(ctx *blockCtx, v *ast.ForPhraseStmt) {
 		compileExpr(ctx, v.Cond)
 		cb.Then()
 		compileStmts(ctx, v.Body.List)
-		cb.SetComments(comments, true)
+		cb.SetComments(comments, once)
 		cb.End()
 	} else {
 		compileStmts(ctx, v.Body.List)
+		cb.SetComments(comments, once)
 	}
-	cb.SetComments(comments, true)
 	setBodyHandler(ctx)
 	cb.End()
 }
@@ -409,7 +409,7 @@ func toForStmt(forPos token.Pos, value ast.Expr, body *ast.BlockStmt, re *ast.Ra
 // end
 func compileForStmt(ctx *blockCtx, v *ast.ForStmt) {
 	cb := ctx.cb
-	comments := cb.Comments()
+	comments, once := cb.BackupComments()
 	cb.For()
 	if v.Init != nil {
 		compileStmt(ctx, v.Init)
@@ -425,7 +425,7 @@ func compileForStmt(ctx *blockCtx, v *ast.ForStmt) {
 		cb.Post()
 		compileStmt(ctx, v.Post)
 	}
-	cb.SetComments(comments, true)
+	cb.SetComments(comments, once)
 	setBodyHandler(ctx)
 	cb.End()
 }
@@ -435,7 +435,7 @@ func compileForStmt(ctx *blockCtx, v *ast.ForStmt) {
 // end
 func compileIfStmt(ctx *blockCtx, v *ast.IfStmt) {
 	cb := ctx.cb
-	comments := cb.Comments()
+	comments, once := cb.BackupComments()
 	cb.If()
 	if v.Init != nil {
 		compileStmt(ctx, v.Init)
@@ -451,7 +451,7 @@ func compileIfStmt(ctx *blockCtx, v *ast.IfStmt) {
 			compileStmt(ctx, e)
 		}
 	}
-	cb.SetComments(comments, true)
+	cb.SetComments(comments, once)
 	cb.End()
 }
 
@@ -465,7 +465,7 @@ func compileIfStmt(ctx *blockCtx, v *ast.IfStmt) {
 // end
 func compileTypeSwitchStmt(ctx *blockCtx, v *ast.TypeSwitchStmt) {
 	var cb = ctx.cb
-	comments := cb.Comments()
+	comments, once := cb.BackupComments()
 	var name string
 	var ta *ast.TypeAssertExpr
 	switch stmt := v.Assign.(type) {
@@ -529,7 +529,7 @@ func compileTypeSwitchStmt(ctx *blockCtx, v *ast.TypeSwitchStmt) {
 		commentStmt(ctx, stmt)
 		cb.End()
 	}
-	cb.SetComments(comments, true)
+	cb.SetComments(comments, once)
 	cb.End()
 }
 
@@ -543,7 +543,7 @@ func compileTypeSwitchStmt(ctx *blockCtx, v *ast.TypeSwitchStmt) {
 // end
 func compileSwitchStmt(ctx *blockCtx, v *ast.SwitchStmt) {
 	cb := ctx.cb
-	comments := cb.Comments()
+	comments, once := cb.BackupComments()
 	cb.Switch()
 	if v.Init != nil {
 		compileStmt(ctx, v.Init)
@@ -604,7 +604,7 @@ func compileSwitchStmt(ctx *blockCtx, v *ast.SwitchStmt) {
 		commentStmt(ctx, stmt)
 		cb.End()
 	}
-	cb.SetComments(comments, true)
+	cb.SetComments(comments, once)
 	cb.End()
 }
 
@@ -631,7 +631,7 @@ func hasFallthrough(body []ast.Stmt) ([]ast.Stmt, bool) {
 // end
 func compileSelectStmt(ctx *blockCtx, v *ast.SelectStmt) {
 	cb := ctx.cb
-	comments := cb.Comments()
+	comments, once := cb.BackupComments()
 	cb.Select()
 	for _, stmt := range v.Body.List {
 		c, ok := stmt.(*ast.CommClause)
@@ -648,7 +648,7 @@ func compileSelectStmt(ctx *blockCtx, v *ast.SelectStmt) {
 		commentStmt(ctx, stmt)
 		cb.End()
 	}
-	cb.SetComments(comments, true)
+	cb.SetComments(comments, once)
 	cb.End()
 }
 
diff --git a/cmd/gopdecl/goenv.go b/cmd/gopdecl/goenv.go
new file mode 100644
index 00000000..f8429603
--- /dev/null
+++ b/cmd/gopdecl/goenv.go
@@ -0,0 +1,16 @@
+package main
+
+import (
+	"os"
+	"path/filepath"
+	"runtime"
+)
+
+func initGoModCache() string {
+	val := os.Getenv("GOMODCACHE")
+	if val == "" {
+		val = filepath.Join(runtime.GOROOT(), "pkg/mod")
+		os.Setenv("GOMODCACHE", val)
+	}
+	return val
+}
diff --git a/cmd/gopdecl/gopdecl.go b/cmd/gopdecl/gopdecl.go
new file mode 100644
index 00000000..cd5772de
--- /dev/null
+++ b/cmd/gopdecl/gopdecl.go
@@ -0,0 +1,133 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"go/ast"
+	"go/importer"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"io/fs"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+	"unicode"
+
+	"github.com/goplus/gop/ast/togo"
+	gopp "github.com/goplus/gop/parser"
+	"golang.org/x/tools/go/gcexportdata"
+)
+
+var (
+	compiler = flag.String("c", "source", `compiler to use: source, gc or gccgo`)
+	internal = flag.Bool("i", false, "print internal declarations")
+)
+
+func usage() {
+	fmt.Fprintf(os.Stderr, "Usage: gopdecl [-i] [source.gop ...]\n")
+	flag.PrintDefaults()
+}
+
+func isDir(name string) bool {
+	if fi, err := os.Lstat(name); err == nil {
+		return fi.IsDir()
+	}
+	return false
+}
+
+func isPublic(name string) bool {
+	for _, c := range name {
+		return unicode.IsUpper(c)
+	}
+	return false
+}
+
+var (
+	goModCache string
+)
+
+func main() {
+	flag.Parse()
+	if flag.NArg() < 1 {
+		usage()
+		return
+	}
+	goModCache = initGoModCache()
+
+	var fset = token.NewFileSet()
+	var files []*ast.File
+	var imp types.Importer
+	switch *compiler {
+	case "gc":
+		packages := make(map[string]*types.Package)
+		imp = gcexportdata.NewImporter(fset, packages)
+	case "source":
+		imp = importer.ForCompiler(fset, "source", nil)
+	default:
+		log.Panicln("Unsupport compiler:", *compiler)
+	}
+
+	if infile := flag.Arg(0); isDir(infile) {
+		pkgs, first := gopp.ParseDir(fset, infile, func(fi fs.FileInfo) bool {
+			name := fi.Name()
+			if strings.HasPrefix(name, "_") {
+				return false
+			}
+			return !strings.HasSuffix(strings.TrimSuffix(name, filepath.Ext(name)), "_test")
+		}, 0)
+		check(first)
+		for name, pkg := range pkgs {
+			if !strings.HasSuffix(name, "_test") {
+				for _, f := range pkg.Files {
+					files = append(files, togo.ASTFile(f, 0))
+				}
+				break
+			}
+		}
+	} else {
+		for i, n := 0, flag.NArg(); i < n; i++ {
+			infile = flag.Arg(i)
+			switch filepath.Ext(infile) {
+			case ".gop":
+				f, err := gopp.ParseFile(fset, infile, nil, 0)
+				check(err)
+				files = append(files, togo.ASTFile(f, 0))
+			case ".go":
+				f, err := parser.ParseFile(fset, infile, nil, 0)
+				check(err)
+				files = append(files, f)
+			default:
+				log.Panicln("Unknown support file:", infile)
+			}
+		}
+	}
+
+	conf := &types.Config{
+		Importer:         imp,
+		IgnoreFuncBodies: true,
+		Error: func(err error) {
+			log.Println(err)
+		},
+		DisableUnusedImportCheck: true,
+	}
+
+	pkg, err := conf.Check("", fset, files, nil)
+	check(err)
+
+	scope := pkg.Scope()
+	names := scope.Names()
+	for _, name := range names {
+		if *internal || isPublic(name) {
+			fmt.Println(scope.Lookup(name))
+		}
+	}
+}
+
+func check(err error) {
+	if err != nil {
+		fmt.Fprintln(os.Stderr, err)
+		os.Exit(1)
+	}
+}
diff --git a/cmd/internal/build/build.go b/cmd/internal/build/build.go
index 46c9d3ee..011ed809 100644
--- a/cmd/internal/build/build.go
+++ b/cmd/internal/build/build.go
@@ -18,13 +18,16 @@
 package build
 
 import (
+	"fmt"
 	"os"
+	"os/exec"
 
 	"github.com/qiniu/x/log"
 
 	"github.com/goplus/gop/cl"
 	"github.com/goplus/gop/cmd/internal/base"
-	"github.com/goplus/gop/x/gengo"
+	"github.com/goplus/gop/x/gopproj"
+	"github.com/goplus/gop/x/gopprojs"
 	"github.com/goplus/gox"
 )
 
@@ -36,7 +39,7 @@ var Cmd = &base.Command{
 
 var (
 	flagVerbose = flag.Bool("v", false, "print verbose information.")
-	flagOutput  = flag.String("o", "", "gop build output file.")
+	flagOutput  = flag.String("o", "a.out", "gop build output file.")
 	flag        = &Cmd.Flag
 )
 
@@ -50,22 +53,46 @@ func runCmd(_ *base.Command, args []string) {
 		log.Fatalln("parse input arguments failed:", err)
 	}
 
-	pattern := flag.Args()
-	if len(pattern) == 0 {
-		pattern = []string{"."}
-	}
-
 	if *flagVerbose {
 		gox.SetDebug(gox.DbgFlagAll &^ gox.DbgFlagComments)
 		cl.SetDebug(cl.DbgFlagAll)
 		cl.SetDisableRecover(true)
 	}
-	_ = flagOutput
 
-	if !gengo.GenGo(gengo.Config{}, false, ".", pattern...) {
+	args = flag.Args()
+	if len(args) == 0 {
+		args = []string{"."}
+	}
+	gopBuild(args)
+}
+
+func gopBuild(args []string) {
+	proj, args, err := gopprojs.ParseOne(args...)
+	if err != nil {
+		log.Fatalln(err)
+	}
+
+	flags := 0
+	ctx, goProj, err := gopproj.OpenProject(flags, proj)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, err)
 		os.Exit(1)
 	}
-	base.RunGoCmd(".", "build", args...)
+	goProj.BuildArgs = []string{"-o", *flagOutput}
+	cmd := ctx.GoCommand("build", goProj)
+	cmd.Stdin = os.Stdin
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	cmd.Env = os.Environ()
+	err = cmd.Run()
+	if err != nil {
+		switch e := err.(type) {
+		case *exec.ExitError:
+			os.Exit(e.ExitCode())
+		default:
+			log.Fatalln(err)
+		}
+	}
 }
 
 // -----------------------------------------------------------------------------
diff --git a/cmd/internal/run/run.go b/cmd/internal/run/run.go
index ef749d7a..02432514 100644
--- a/cmd/internal/run/run.go
+++ b/cmd/internal/run/run.go
@@ -93,7 +93,7 @@ func gopRun(args []string) {
 	}
 	ctx, goProj, err := gopproj.OpenProject(flags, proj)
 	if err != nil {
-		fmt.Fprintln(os.Stderr, "OpenProject failed:", err)
+		fmt.Fprintln(os.Stderr, err)
 		os.Exit(1)
 	}
 	goProj.ExecArgs = args
diff --git a/go.mod b/go.mod
index 3363e022..6c6e02df 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,8 @@ module github.com/goplus/gop
 go 1.16
 
 require (
-	github.com/goplus/gox v1.9.6
+	github.com/goplus/gox v1.10.7
 	github.com/qiniu/x v1.11.5
-	golang.org/x/mod v0.5.1
+	golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3
+	golang.org/x/tools v0.1.10
 )
diff --git a/go.sum b/go.sum
index 97dedd65..e785d092 100644
--- a/go.sum
+++ b/go.sum
@@ -1,31 +1,32 @@
-github.com/goplus/gox v1.9.6 h1:02w1XDGjpIqrELpSWMhQJ3khiVByo4LlLrcxPxvh1kw=
-github.com/goplus/gox v1.9.6/go.mod h1:PznHkzl2HARBf7+s2reqcwKm1Z1a6Wae6EntQEh0iJI=
+github.com/goplus/gox v1.10.7 h1:OeoppVRhcNuuze2Dxy3fwY26iKB8O39Y/Ba5TuKuH9Y=
+github.com/goplus/gox v1.10.7/go.mod h1:gu7fuQF8RmWPZUjd+tEJGuV3m/vOEv0bHrct0x/KatM=
 github.com/qiniu/x v1.11.5 h1:TYr5cl4g2yoHAZeDK4MTjKF6CMoG+IHlCDvvM5qym6U=
 github.com/qiniu/x v1.11.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
 github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
-golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
+golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=
 golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
-golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
+golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
+golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
diff --git a/parser/parser.go b/parser/parser.go
index fab81edf..304387a6 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -1588,6 +1588,9 @@ func (p *parser) parseCallOrConversion(fun ast.Expr, isCmd bool) *ast.CallExpr {
 				break
 			}
 		}
+		if isCmd && p.tok == token.RBRACE {
+			break
+		}
 		if !p.atComma("argument list", endTok) {
 			break
 		}
diff --git a/parser/parser_gop.go b/parser/parser_gop.go
index a8785391..4273085f 100644
--- a/parser/parser_gop.go
+++ b/parser/parser_gop.go
@@ -20,6 +20,7 @@ import (
 	"bytes"
 	"errors"
 	"io"
+	"io/fs"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -49,19 +50,19 @@ func SetDebug(dbgFlags int) {
 
 // FileSystem represents a file system.
 type FileSystem interface {
-	ReadDir(dirname string) ([]os.FileInfo, error)
+	ReadDir(dirname string) ([]fs.FileInfo, error)
 	ReadFile(filename string) ([]byte, error)
 	Join(elem ...string) string
 }
 
 type localFS struct{}
 
-func (p localFS) ReadDir(dirname string) ([]os.FileInfo, error) {
+func (p localFS) ReadDir(dirname string) ([]fs.FileInfo, error) {
 	return ioutil.ReadDir(dirname)
 }
 
 func (p localFS) ReadFile(filename string) ([]byte, error) {
-	return ioutil.ReadFile(filename)
+	return os.ReadFile(filename)
 }
 
 func (p localFS) Join(elem ...string) string {
@@ -96,13 +97,13 @@ func astFileToPkg(file *ast.File, fileName string) (pkg *ast.Package) {
 
 // ParseDir calls ParseFSDir by passing a local filesystem.
 //
-func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, mode Mode) (pkgs map[string]*ast.Package, first error) {
+func ParseDir(fset *token.FileSet, path string, filter func(fs.FileInfo) bool, mode Mode) (pkgs map[string]*ast.Package, first error) {
 	return ParseFSDir(fset, local, path, Config{Filter: filter, Mode: mode})
 }
 
 type Config struct {
 	IsClass func(ext string) (isProj bool, ok bool)
-	Filter  func(os.FileInfo) bool
+	Filter  func(fs.FileInfo) bool
 	Mode    Mode
 }
 
@@ -116,7 +117,7 @@ func ParseDirEx(fset *token.FileSet, path string, conf Config) (pkgs map[string]
 // directory specified by path and returns a map of package name -> package
 // AST with all the packages found.
 //
-// If filter != nil, only the files with os.FileInfo entries passing through
+// If filter != nil, only the files with fs.FileInfo entries passing through
 // the filter (and ending in ".gop") are considered. The mode bits are passed
 // to ParseFile unchanged. Position information is recorded in fset, which
 // must not be nil.
diff --git a/x/gengo/gengo.go b/x/gengo/gengo.go
index 446968cc..def17b5a 100644
--- a/x/gengo/gengo.go
+++ b/x/gengo/gengo.go
@@ -186,27 +186,13 @@ func (p *Runner) genGoPkgs(conf *Config) {
 		p.state = stateDone
 		return
 	}
-	imps := getKeys(imports)
 	modRoot := p.modRoot
-	impConf := &packages.Config{
-		ModRoot: modRoot,
-		ModPath: p.modPath,
-		Loaded:  conf.Loaded,
-		Fset:    conf.Fset,
-	}
 	inModCache := modfetch.InModCachePath(modRoot)
 	if inModCache {
 		os.Chmod(modRoot, 0755)
 		defer os.Chmod(modRoot, 0555)
 	}
-	imp, _, err := packages.NewImporter(impConf, imps...)
-	if err != nil {
-		conf.OnErr("newImporter", err)
-		p.state = stateOccurErrors
-		return
-	}
-	defer imp.Close()
-	conf.OnInfo("newImporter: %v\n", imps)
+	imp := packages.NewImporter(conf.Fset)
 	for _, pkg := range p.pkgs {
 		if pkg.flags == pkgFlagChanged {
 			p.genGoPkg(pkg, imp, conf)
diff --git a/x/gopmod/module.go b/x/gopmod/module.go
index cd0c5db7..49e068f9 100644
--- a/x/gopmod/module.go
+++ b/x/gopmod/module.go
@@ -18,6 +18,7 @@ package gopmod
 
 import (
 	"fmt"
+	"io/fs"
 	"os"
 	"path"
 	"path/filepath"
@@ -411,7 +412,7 @@ func (p *Module) ChangeInfo(dir string) (ci ChangeInfo, err error) {
 		return
 	}
 	dir += "/"
-	var fi os.FileInfo
+	var fi fs.FileInfo
 	for _, d := range list {
 		fname := d.Name()
 		if strings.HasPrefix(fname, "_") || d.IsDir() {
diff --git a/x/gopproj/gopfiles.go b/x/gopproj/gopfiles.go
index b2e51548..dcbccde4 100644
--- a/x/gopproj/gopfiles.go
+++ b/x/gopproj/gopfiles.go
@@ -28,17 +28,19 @@ import (
 	"github.com/goplus/gop/parser"
 	"github.com/goplus/gop/token"
 	"github.com/goplus/gox"
+	"github.com/goplus/gox/packages"
 )
 
 // -----------------------------------------------------------------------------
 
 type gopFiles struct {
-	files []string
+	files  []string
+	defctx bool
 }
 
 func (p *Context) openFromGopFiles(files []string) (proj *Project, err error) {
 	proj = &Project{
-		Source: &gopFiles{files: files},
+		Source: &gopFiles{files: files, defctx: p.defctx},
 	}
 	if len(files) == 1 {
 		file := files[0]
@@ -118,6 +120,14 @@ func (p *gopFiles) GenGo(outFile, modFile string) error {
 	srcDir, _ := filepath.Split(outFile)
 	modDir, _ := filepath.Split(modFile)
 	conf := &cl.Config{WorkingDir: modDir, TargetDir: srcDir, Fset: fset}
+	if p.defctx {
+		wd, _ := os.Getwd()
+		os.Chdir(modDir)
+		defer func() {
+			os.Chdir(wd)
+		}()
+	}
+	conf.Importer = packages.NewImporter(fset)
 	out, err := cl.NewPackage("", mainPkg, conf)
 	if err != nil {
 		return err
diff --git a/x/gopproj/gopproj.go b/x/gopproj/gopproj.go
index ac1c5722..23e10eb5 100644
--- a/x/gopproj/gopproj.go
+++ b/x/gopproj/gopproj.go
@@ -41,8 +41,11 @@ func (p *handleEvent) OnInfo(format string, args ...interface{}) {
 }
 
 func (p *handleEvent) OnErr(stage string, err error) {
-	p.lastErr = fmt.Errorf("%s: %v", stage, err)
-	fmt.Fprintln(os.Stderr, p.lastErr)
+	lastErr := fmt.Errorf("%s: %w", stage, err)
+	if p.lastErr != nil {
+		fmt.Fprintln(os.Stderr, p.lastErr)
+	}
+	p.lastErr = lastErr
 }
 
 func (p *handleEvent) OnEnd() {
@@ -72,8 +75,10 @@ func OpenProject(flags int, src gopprojs.Proj) (ctx *Context, proj *Project, err
 		proj, err = ctx.OpenFiles(flags, v.Files...)
 		return
 	case *gopprojs.DirProj:
+		os.Chdir(v.Dir)
 		return OpenDir(flags, v.Dir)
 	case *gopprojs.PkgPathProj:
+		os.Chdir(modfetch.GOMODCACHE)
 		return OpenPkgPath(flags, v.Path)
 	}
 	panic("OpenProject: unexpected source")
diff --git a/x/gopproj/projctx.go b/x/gopproj/projctx.go
index 1262007f..dc74da86 100644
--- a/x/gopproj/projctx.go
+++ b/x/gopproj/projctx.go
@@ -211,10 +211,17 @@ func runCommand(dir, command string, args ...string) error {
 func genDefaultGopMod(modfile string) {
 	dir, _ := filepath.Split(modfile)
 	dummy := dir + "dummy"
+	tmodfile := dummy + "/go.mod"
 	os.MkdirAll(dummy, 0755)
-	genGomodFile(modfile)
+	genGomodFile(tmodfile)
 	genDummyProject(dummy)
-	execCommand(dir, "go", "mod", "tidy")
+	execCommand(dummy, "go", "mod", "tidy")
+	os.Rename(tmodfile, modfile)
+	os.Rename(toGoSum(tmodfile), toGoSum(modfile))
+}
+
+func toGoSum(modfile string) string {
+	return modfile[:len(modfile)-3] + "sum"
 }
 
 // -----------------------------------------------------------------------------


Test patch:
diff --git a/cl/builtin_test.go b/cl/builtin_test.go
index 1fe44902..1af6b22e 100644
--- a/cl/builtin_test.go
+++ b/cl/builtin_test.go
@@ -33,18 +33,7 @@ var (
 
 func getGoxConf() *gox.Config {
 	fset := token.NewFileSet()
-	conf := &packages.Config{
-		ModPath: "github.com/goplus/gop/cl",
-		Loaded:  make(map[string]*types.Package),
-		Fset:    fset,
-	}
-	const (
-		pkgspx2 = "github.com/goplus/gop/cl/internal/spx2"
-	)
-	imp, _, err := packages.NewImporter(conf, ".", pkgspx2)
-	if err != nil {
-		panic(err)
-	}
+	imp := packages.NewImporter(fset)
 	return &gox.Config{Fset: fset, Importer: imp}
 }
 
diff --git a/cl/compile_spx_test.go b/cl/compile_spx_test.go
index 7c5c264f..f0dbfc04 100644
--- a/cl/compile_spx_test.go
+++ b/cl/compile_spx_test.go
@@ -70,29 +70,35 @@ func gopSpxTest(t *testing.T, gmx, spxcode, expected string) {
 }
 
 func gopSpxTestEx(t *testing.T, gmx, spxcode, expected, gmxfile, spxfile string) {
-	cl.SetDisableRecover(true)
-	defer cl.SetDisableRecover(false)
-
-	fs := newTwoFileFS("/foo", spxfile, spxcode, gmxfile, gmx)
-	pkgs, err := parser.ParseFSDir(gblFset, fs, "/foo", spxParserConf())
-	if err != nil {
-		scanner.PrintError(os.Stderr, err)
-		t.Fatal("ParseFSDir:", err)
-	}
-	bar := pkgs["main"]
-	pkg, err := cl.NewPackage("", bar, gblConf)
-	if err != nil {
-		t.Fatal("NewPackage:", err)
-	}
-	var b bytes.Buffer
-	err = gox.WriteTo(&b, pkg, false)
-	if err != nil {
-		t.Fatal("gox.WriteTo failed:", err)
-	}
-	result := b.String()
-	if result != expected {
-		t.Fatalf("\nResult:\n%s\nExpected:\n%s\n", result, expected)
-	}
+	gopSpxTestExConf(t, "gopSpxTest", gblConf, gmx, spxcode, expected, gmxfile, spxfile)
+}
+
+func gopSpxTestExConf(t *testing.T, name string, conf *cl.Config, gmx, spxcode, expected, gmxfile, spxfile string) {
+	t.Run(name, func(t *testing.T) {
+		cl.SetDisableRecover(true)
+		defer cl.SetDisableRecover(false)
+
+		fs := newTwoFileFS("/foo", spxfile, spxcode, gmxfile, gmx)
+		pkgs, err := parser.ParseFSDir(gblFset, fs, "/foo", spxParserConf())
+		if err != nil {
+			scanner.PrintError(os.Stderr, err)
+			t.Fatal("ParseFSDir:", err)
+		}
+		bar := pkgs["main"]
+		pkg, err := cl.NewPackage("", bar, conf)
+		if err != nil {
+			t.Fatal("NewPackage:", err)
+		}
+		var b bytes.Buffer
+		err = gox.WriteTo(&b, pkg, false)
+		if err != nil {
+			t.Fatal("gox.WriteTo failed:", err)
+		}
+		result := b.String()
+		if result != expected {
+			t.Fatalf("\nResult:\n%s\nExpected:\n%s\n", result, expected)
+		}
+	})
 }
 
 func gopSpxErrorTestEx(t *testing.T, msg, gmx, spxcode, gmxfile, spxfile string) {
@@ -213,7 +219,6 @@ type index struct {
 func (this *index) bar() {
 }
 func (this *index) onInit() {
-	Foo
 	this.bar()
 	fmt.Println("Hi")
 }
@@ -297,16 +302,18 @@ type Game struct {
 	*spx.MyGame
 	Kai Kai
 }
+
+func (this *Game) onInit() {
+	this.Kai.Clone()
+	this.Broadcast__0("msg1")
+}
+
 type Kai struct {
 	spx.Sprite
 	*Game
 	a int
 }
 
-func (this *Game) onInit() {
-	this.Kai.Clone()
-	this.Broadcast__0("msg1")
-}
 func (this *Kai) onInit() {
 	this.a = 1
 }
@@ -342,10 +349,6 @@ type index struct {
 	Kai Kai
 	t   spx.Sound
 }
-type Kai struct {
-	spx.Sprite
-	*index
-}
 
 func (this *index) MainEntry() {
 	spx.Gopt_MyGame_Run(this, "hzip://open.qiniu.us/weather/res.zip")
@@ -353,6 +356,12 @@ func (this *index) MainEntry() {
 func main() {
 	spx.Gopt_MyGame_Main(new(index))
 }
+
+type Kai struct {
+	spx.Sprite
+	*index
+}
+
 func (this *Kai) Main() {
 	fmt.Println("Hi")
 }
@@ -392,3 +401,94 @@ func (this *Kai) onMsg(msg string) {
 }
 `, "Game.t2gmx", "Kai.t2spx")
 }
+
+func TestSpxMainEntry(t *testing.T) {
+	conf := *gblConf
+	conf.NoAutoGenMain = false
+
+	gopSpxTestExConf(t, "Nocode", &conf, `
+`, `
+`, `package main
+
+import spx2 "github.com/goplus/gop/cl/internal/spx2"
+
+type Game struct {
+	spx2.Game
+}
+
+func (this *Game) MainEntry() {
+}
+func main() {
+	new(Game).Main()
+}
+`, "Game.t2gmx", "Kai.t2spx")
+	gopSpxTestExConf(t, "OnlyGmx", &conf, `
+var (
+	Kai Kai
+)
+`, `
+`, `package main
+
+import spx2 "github.com/goplus/gop/cl/internal/spx2"
+
+type Game struct {
+	spx2.Game
+	Kai Kai
+}
+
+func (this *Game) MainEntry() {
+}
+func main() {
+	new(Game).Main()
+}
+
+type Kai struct {
+	spx2.Sprite
+	*Game
+}
+`, "Game.t2gmx", "Kai.t2spx")
+
+	gopSpxTestExConf(t, "KaiAndGmx", &conf, `
+var (
+	Kai Kai
+)
+func MainEntry() {
+	println "Hi"
+}
+`, `
+func Main() {
+	println "Hello"
+}
+func onMsg(msg string) {
+}
+`, `package main
+
+import (
+	fmt "fmt"
+	spx2 "github.com/goplus/gop/cl/internal/spx2"
+)
+
+type Game struct {
+	spx2.Game
+	Kai Kai
+}
+
+func (this *Game) MainEntry() {
+	fmt.Println("Hi")
+}
+func main() {
+	new(Game).Main()
+}
+
+type Kai struct {
+	spx2.Sprite
+	*Game
+}
+
+func (this *Kai) Main() {
+	fmt.Println("Hello")
+}
+func (this *Kai) onMsg(msg string) {
+}
+`, "Game.t2gmx", "Kai.t2spx")
+}
diff --git a/cl/compile_test.go b/cl/compile_test.go
index 07ff352f..cadc4fe8 100644
--- a/cl/compile_test.go
+++ b/cl/compile_test.go
@@ -18,7 +18,6 @@ package cl_test
 
 import (
 	"bytes"
-	"go/types"
 	"os"
 	"strings"
 	"sync"
@@ -42,36 +41,27 @@ func init() {
 	gox.SetDebug(gox.DbgFlagAll)
 	cl.SetDebug(cl.DbgFlagAll)
 	gblFset = token.NewFileSet()
-	conf := &packages.Config{
-		ModRoot: ".",
-		ModPath: "github.com/goplus/gop/cl",
-		Loaded:  make(map[string]*types.Package),
-		Fset:    gblFset,
-	}
-	const (
-		pkgbi   = "github.com/goplus/gop/builtin"
-		pkggt   = "github.com/goplus/gop/ast/goptest"
-		pkgspx  = "github.com/goplus/gop/cl/internal/spx"
-		pkgspx2 = "github.com/goplus/gop/cl/internal/spx2"
-	)
-	imp, _, err := packages.NewImporter(
-		conf, ".", "encoding/base32", "encoding/base64", pkgbi, pkggt, pkgspx, pkgspx2)
-	if err != nil {
-		panic(err)
-	}
+	imp := packages.NewImporter(gblFset)
 	gblConf = &cl.Config{
-		Fset:        gblFset,
-		Importer:    imp,
-		LookupClass: lookupClass,
-		NoFileLine:  true,
+		Fset:          gblFset,
+		Importer:      imp,
+		LookupClass:   lookupClass,
+		NoFileLine:    true,
+		NoAutoGenMain: true,
 	}
 }
 
+func gopClNamedTest(t *testing.T, name string, gopcode, expected string) {
+	t.Run(name, func(t *testing.T) {
+		gopClTest(t, gopcode, expected)
+	})
+}
+
 func gopClTest(t *testing.T, gopcode, expected string) {
-	gopClTestEx(t, "main", gopcode, expected)
+	gopClTestEx(t, gblConf, "main", gopcode, expected)
 }
 
-func gopClTestEx(t *testing.T, pkgname, gopcode, expected string) {
+func gopClTestEx(t *testing.T, conf *cl.Config, pkgname, gopcode, expected string) {
 	cl.SetDisableRecover(true)
 	defer cl.SetDisableRecover(false)
 
@@ -82,7 +72,7 @@ func gopClTestEx(t *testing.T, pkgname, gopcode, expected string) {
 		t.Fatal("ParseFSDir:", err)
 	}
 	bar := pkgs[pkgname]
-	pkg, err := cl.NewPackage("github.com/goplus/gop/cl", bar, gblConf)
+	pkg, err := cl.NewPackage("github.com/goplus/gop/cl", bar, conf)
 	if err != nil {
 		t.Fatal("NewPackage:", err)
 	}
@@ -156,14 +146,14 @@ const (
 )
 `, `package main
 
-func main() {
-	const a = append + len
-}
-
 const (
 	append = iota
 	len
 )
+
+func main() {
+	const a = append + len
+}
 `)
 }
 
@@ -548,7 +538,7 @@ func main() {
 }
 
 func TestIssue774(t *testing.T) {
-	gopClTest(t, `
+	gopClNamedTest(t, "InterfaceTypeAssert", `
 package main
 
 import "fmt"
@@ -578,9 +568,6 @@ func main() {
 	fmt.Println(a.(*A))
 }
 
-type AA interface {
-	String() string
-}
 type A struct {
 	str string
 }
@@ -588,8 +575,12 @@ type A struct {
 func (a *A) String() string {
 	return a.str
 }
+
+type AA interface {
+	String() string
+}
 `)
-	gopClTest(t, `
+	gopClNamedTest(t, "getInterface", `
 package main
 
 import "fmt"
@@ -623,16 +614,14 @@ func main() {
 	a := get()
 	fmt.Println(a.(*A))
 }
-
-type AA interface {
-	String() string
-}
-
 func get() AA {
 	var a AA
 	return a
 }
 
+type AA interface {
+	String() string
+}
 type A struct {
 	str string
 }
@@ -1203,16 +1192,42 @@ var x, y = 1, "Hi"
 `)
 }
 
+func TestUint128Add(t *testing.T) {
+	gopClTest(t, `
+var x, y uint128
+var z uint128 = x + y
+`, `package main
+
+import ng "github.com/goplus/gop/builtin/ng"
+
+var x, y ng.Uint128
+var z ng.Uint128 = x.Gop_Add__1(y)
+`)
+}
+
+func TestInt128Add(t *testing.T) {
+	gopClTest(t, `
+var x, y int128
+var z int128 = x + y
+`, `package main
+
+import ng "github.com/goplus/gop/builtin/ng"
+
+var x, y ng.Int128
+var z ng.Int128 = x.Gop_Add__1(y)
+`)
+}
+
 func TestBigIntAdd(t *testing.T) {
 	gopClTest(t, `
 var x, y bigint
 var z bigint = x + y
 `, `package main
 
-import builtin "github.com/goplus/gop/builtin"
+import ng "github.com/goplus/gop/builtin/ng"
 
-var x, y builtin.Gop_bigint
-var z builtin.Gop_bigint = x.Gop_Add(y)
+var x, y ng.Bigint
+var z ng.Bigint = x.Gop_Add(y)
 `)
 }
 
@@ -1222,11 +1237,33 @@ var x = 1r
 `, `package main
 
 import (
-	builtin "github.com/goplus/gop/builtin"
+	ng "github.com/goplus/gop/builtin/ng"
 	big "math/big"
 )
 
-var x = builtin.Gop_bigint_Init__1(big.NewInt(1))
+var x = ng.Bigint_Init__1(big.NewInt(1))
+`)
+}
+
+func TestUint128Lit(t *testing.T) {
+	gopClTest(t, `
+var x uint128 = 1
+`, `package main
+
+import ng "github.com/goplus/gop/builtin/ng"
+
+var x ng.Uint128 = ng.Uint128_Init__0(1)
+`)
+}
+
+func TestInt128Lit(t *testing.T) {
+	gopClTest(t, `
+var x int128 = 1
+`, `package main
+
+import ng "github.com/goplus/gop/builtin/ng"
+
+var x ng.Int128 = ng.Int128_Init__0(1)
 `)
 }
 
@@ -1236,11 +1273,11 @@ var x = 1/2r
 `, `package main
 
 import (
-	builtin "github.com/goplus/gop/builtin"
+	ng "github.com/goplus/gop/builtin/ng"
 	big "math/big"
 )
 
-var x = builtin.Gop_bigrat_Init__2(big.NewRat(1, 2))
+var x = ng.Bigrat_Init__2(big.NewRat(1, 2))
 `)
 }
 
@@ -1250,11 +1287,11 @@ var x = 3 + 1/2r
 `, `package main
 
 import (
-	builtin "github.com/goplus/gop/builtin"
+	ng "github.com/goplus/gop/builtin/ng"
 	big "math/big"
 )
 
-var x = builtin.Gop_bigrat_Init__2(big.NewRat(7, 2))
+var x = ng.Bigrat_Init__2(big.NewRat(7, 2))
 `)
 }
 
@@ -1267,13 +1304,13 @@ var z = 100 + y
 `, `package main
 
 import (
-	builtin "github.com/goplus/gop/builtin"
+	ng "github.com/goplus/gop/builtin/ng"
 	big "math/big"
 )
 
-var x = builtin.Gop_bigrat_Init__2(big.NewRat(7, 2))
-var y = x.Gop_Add(builtin.Gop_bigrat_Init__0(100))
-var z = builtin.Gop_bigrat_Init__0(100) + y
+var x = ng.Bigrat_Init__2(big.NewRat(7, 2))
+var y = x.Gop_Add(ng.Bigrat_Init__0(100))
+var z = ng.Bigrat_Init__0(100) + y
 `)
 }
 
@@ -1355,12 +1392,12 @@ var x bigint
 x += 3
 `, `package main
 
-import builtin "github.com/goplus/gop/builtin"
+import ng "github.com/goplus/gop/builtin/ng"
 
-var x builtin.Gop_bigint
+var x ng.Bigint
 
 func main() {
-	x.Gop_AddAssign(builtin.Gop_bigint_Init__0(3))
+	x.Gop_AddAssign(ng.Bigint_Init__0(3))
 }
 `)
 }
@@ -1372,13 +1409,13 @@ x *= 2
 `, `package main
 
 import (
-	builtin "github.com/goplus/gop/builtin"
+	ng "github.com/goplus/gop/builtin/ng"
 	big "math/big"
 )
 
 func main() {
-	x := builtin.Gop_bigint_Init__1(big.NewInt(3))
-	x.Gop_MulAssign(builtin.Gop_bigint_Init__0(2))
+	x := ng.Bigint_Init__1(big.NewInt(3))
+	x.Gop_MulAssign(ng.Bigint_Init__0(2))
 }
 `)
 }
@@ -1390,13 +1427,13 @@ x *= 2r
 `, `package main
 
 import (
-	builtin "github.com/goplus/gop/builtin"
+	ng "github.com/goplus/gop/builtin/ng"
 	big "math/big"
 )
 
 func main() {
-	x := builtin.Gop_bigint_Init__1(big.NewInt(3))
-	x.Gop_MulAssign(builtin.Gop_bigint_Init__1(big.NewInt(2)))
+	x := ng.Bigint_Init__1(big.NewInt(3))
+	x.Gop_MulAssign(ng.Bigint_Init__1(big.NewInt(2)))
 }
 `)
 }
@@ -1609,12 +1646,12 @@ func main() {
 }
 `, `package main
 
+type bar = foo
 type foo struct {
 	p *foo
 	A int
 	B string "tag1:123"
 }
-type bar = foo
 
 func main() {
 	type a struct {
@@ -1884,10 +1921,6 @@ type fooIter struct {
 	data *foo
 	idx  int
 }
-type foo struct {
-	key []int
-	val []string
-}
 
 func (p *fooIter) Next() (key int, val string, ok bool) {
 	if p.idx < len(p.data.key) {
@@ -1896,6 +1929,12 @@ func (p *fooIter) Next() (key int, val string, ok bool) {
 	}
 	return
 }
+
+type foo struct {
+	key []int
+	val []string
+}
+
 func (p *foo) Gop_Enum() *fooIter {
 	return &fooIter{data: p}
 }
@@ -3229,22 +3268,19 @@ func TestNew(t *testing.T) {
 		t.Fatal("Test failed:", ret, expected)
 	}
 }
-
-type Result struct {
-	Repo Repo
-}
-
 func New() Result {
 	repo := newRepo()
 	return Result{Repo: repo}
 }
+func newRepo() Repo {
+	return Repo{Title: "Hi"}
+}
 
 type Repo struct {
 	Title string
 }
-
-func newRepo() Repo {
-	return Repo{Title: "Hi"}
+type Result struct {
+	Repo Repo
 }
 `)
 	}
@@ -3515,7 +3551,7 @@ func main() {
 	fmt.Println("init")
 }
 `)
-	gopClTestEx(t, "bar", `package bar
+	gopClTestEx(t, gblConf, "bar", `package bar
 println("init")
 `, `package bar
 
@@ -3602,3 +3638,59 @@ func main() {
 }
 `)
 }
+
+func TestAnyAlias(t *testing.T) {
+	gopClTest(t, `
+var a any = 100
+println(a)
+`, `package main
+
+import fmt "fmt"
+
+var a interface {
+} = 100
+
+func main() {
+	fmt.Println(a)
+}
+`)
+}
+
+func TestMainEntry(t *testing.T) {
+	conf := *gblConf
+	conf.NoAutoGenMain = false
+
+	gopClTestEx(t, &conf, "main", `
+`, `package main
+
+func main() {
+}
+`)
+	gopClTestEx(t, &conf, "main", `
+func test() {
+	println "hello"
+}
+`, `package main
+
+import fmt "fmt"
+
+func test() {
+	fmt.Println("hello")
+}
+func main() {
+}
+`)
+
+	gopClTestEx(t, &conf, "main", `
+func main() {
+	println "hello"
+}
+`, `package main
+
+import fmt "fmt"
+
+func main() {
+	fmt.Println("hello")
+}
+`)
+}
diff --git a/cl/error_msg_test.go b/cl/error_msg_test.go
index 07563b08..bdba2d13 100644
--- a/cl/error_msg_test.go
+++ b/cl/error_msg_test.go
@@ -806,3 +806,22 @@ import "fmt"
 n, err := fmt.println
 `)
 }
+
+func TestFiledsNameRedecl(t *testing.T) {
+	codeErrorTest(t, `./bar.gop:6:2: Id redeclared
+	./bar.gop:5:2 other declaration of Id
+./bar.gop:7:2: Id redeclared
+	./bar.gop:5:2 other declaration of Id
+./bar.gop:9:2: name redeclared
+	./bar.gop:8:2 other declaration of name`, `
+type Id struct {
+}
+type A struct {
+	Id   int
+	Id   string
+	Id
+	name string
+	name string
+}
+`)
+}
diff --git a/cl/run_test.go b/cl/run_test.go
new file mode 100644
index 00000000..daa71d4f
--- /dev/null
+++ b/cl/run_test.go
@@ -0,0 +1,182 @@
+package cl_test
+
+import (
+	"bytes"
+	"io"
+	"log"
+	"os"
+	"os/exec"
+	"strconv"
+	"strings"
+	"sync/atomic"
+	"testing"
+
+	"github.com/goplus/gop/cl"
+	"github.com/goplus/gop/parser"
+	"github.com/goplus/gop/parser/parsertest"
+	"github.com/goplus/gop/scanner"
+	"github.com/goplus/gox"
+)
+
+var (
+	tmpDir     string
+	tmpFileIdx int64
+)
+
+func init() {
+	home, err := os.Getwd()
+	check(err)
+
+	tmpDir = home + "/.gop/tmp/"
+	err = os.MkdirAll(tmpDir, 0755)
+	check(err)
+}
+
+func check(err error) {
+	if err != nil {
+		log.Panicln(err)
+	}
+}
+
+func checkWith(err error, stdout, stderr io.Writer) {
+	if err != nil {
+		fatalWith(err, stdout, stderr)
+	}
+}
+
+func fatalWith(err error, stdout, stderr io.Writer) {
+	if o, ok := getBytes(stdout, stderr); ok {
+		os.Stderr.Write(o.Bytes())
+	}
+	log.Panicln(err)
+}
+
+type iBytes interface {
+	Bytes() []byte
+}
+
+func getBytes(stdout, stderr io.Writer) (o iBytes, ok bool) {
+	if o, ok = stderr.(iBytes); ok {
+		return
+	}
+	o, ok = stdout.(iBytes)
+	return
+}
+
+func goRun(t *testing.T, code []byte) string {
+	idx := atomic.AddInt64(&tmpFileIdx, 1)
+	infile := tmpDir + strconv.FormatInt(idx, 10) + ".go"
+	err := os.WriteFile(infile, []byte(code), 0666)
+	check(err)
+
+	var stdout, stderr bytes.Buffer
+	cmd := exec.Command("go", "run", infile)
+	cmd.Dir = tmpDir
+	cmd.Stdout = &stdout
+	cmd.Stderr = &stderr
+	err = cmd.Run()
+	os.Remove(infile)
+	checkWith(err, &stdout, &stderr)
+	return stdout.String()
+}
+
+func genGo(t *testing.T, conf *cl.Config, gopcode string) []byte {
+	cl.SetDisableRecover(true)
+	defer cl.SetDisableRecover(false)
+
+	fs := parsertest.NewSingleFileFS("/foo", "bar.gop", gopcode)
+	pkgs, err := parser.ParseFSDir(gblFset, fs, "/foo", parser.Config{Mode: parser.ParseComments})
+	if err != nil {
+		scanner.PrintError(os.Stderr, err)
+		t.Fatal("ParseFSDir:", err)
+	}
+	pkg, err := cl.NewPackage("", pkgs["main"], conf)
+	if err != nil {
+		t.Fatal("NewPackage:", err)
+	}
+	var b bytes.Buffer
+	err = gox.WriteTo(&b, pkg, false)
+	if err != nil {
+		t.Fatal("gox.WriteTo failed:", err)
+	}
+	return b.Bytes()
+}
+
+func testRun(t *testing.T, gopcode, expected string) {
+	code := genGo(t, gblConf, gopcode)
+	result := goRun(t, code)
+	if result != expected {
+		t.Fatalf("=> Result:\n%s\n=> Expected:\n%s\n", result, expected)
+	}
+}
+
+func testRunType(t *testing.T, typ, gopcodeT, expected string) {
+	gopcode := strings.ReplaceAll(gopcodeT, "$(type)", typ)
+	testRun(t, gopcode, expected)
+}
+
+// -----------------------------------------------------------------------------
+
+const (
+	testType_println_code, testType_println_ret = `
+{
+	var x $(type) = 1
+	println x
+}
+`, `1
+`
+	testType_init_code, testType_init_ret = `
+{
+	var x $(type) = 1 << 65
+	var y = x + 1
+	println x
+	println y
+}
+`, `36893488147419103232
+36893488147419103233
+`
+	testType_cast_code, testType_cast_ret = `
+{
+	println $(type)(1 << 65), $(type)()
+}
+`, `36893488147419103232 0
+`
+	testType_printf_code, testType_printf_ret = `
+{
+	var x $(type) = 1
+	printf "%4d\n", x
+}
+`, `   1
+`
+	testTypescanf_code, testType_scanf_ret = `
+import "fmt"
+{
+	var name string
+	var age $(type)
+	fmt.Sscanf("Kim is 22 years old", "%s is %d years old", &name, &age)
+	println name, age
+}
+`, `Kim 22
+`
+)
+
+const (
+	testType_noscanf_code, testType_noscanf_ret = testType_println_code + testType_init_code + testType_cast_code + testType_printf_code,
+		testType_println_ret + testType_init_ret + testType_cast_ret + testType_printf_ret
+	testType_all_code, testType_all_ret = testTypescanf_code + testType_noscanf_code,
+		testType_scanf_ret + testType_noscanf_ret
+)
+
+func TestUint128_run(t *testing.T) {
+	testRunType(t, "uint128", testType_all_code, testType_all_ret)
+}
+
+func TestInt128_run(t *testing.T) {
+	testRunType(t, "int128", testType_all_code, testType_all_ret)
+}
+
+func TestBigint_run(t *testing.T) {
+	testRunType(t, "bigint", testType_noscanf_code, testType_noscanf_ret)
+}
+
+// -----------------------------------------------------------------------------
diff --git a/parser/_testdata/cmdlinestyle4/cmd4.gop b/parser/_testdata/cmdlinestyle4/cmd4.gop
new file mode 100644
index 00000000..b1d457c4
--- /dev/null
+++ b/parser/_testdata/cmdlinestyle4/cmd4.gop
@@ -0,0 +1,5 @@
+func call(fn func(x int)) { fn(100) }
+
+call(func(x int) { println })
+call(func(x int) { println x })
+call(func(x int) { println {"x": 100, "y": 200}, x })
diff --git a/parser/_testdata/cmdlinestyle4/parser.expect b/parser/_testdata/cmdlinestyle4/parser.expect
new file mode 100644
index 00000000..faec429d
--- /dev/null
+++ b/parser/_testdata/cmdlinestyle4/parser.expect
@@ -0,0 +1,164 @@
+package main
+
+file cmd4.gop
+noEntrypoint
+ast.FuncDecl:
+  Name:
+    ast.Ident:
+      Name: call
+  Type:
+    ast.FuncType:
+      Params:
+        ast.FieldList:
+          List:
+            ast.Field:
+              Names:
+                ast.Ident:
+                  Name: fn
+              Type:
+                ast.FuncType:
+                  Params:
+                    ast.FieldList:
+                      List:
+                        ast.Field:
+                          Names:
+                            ast.Ident:
+                              Name: x
+                          Type:
+                            ast.Ident:
+                              Name: int
+  Body:
+    ast.BlockStmt:
+      List:
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: fn
+              Args:
+                ast.BasicLit:
+                  Kind: INT
+                  Value: 100
+ast.FuncDecl:
+  Name:
+    ast.Ident:
+      Name: main
+  Type:
+    ast.FuncType:
+      Params:
+        ast.FieldList:
+  Body:
+    ast.BlockStmt:
+      List:
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: call
+              Args:
+                ast.FuncLit:
+                  Type:
+                    ast.FuncType:
+                      Params:
+                        ast.FieldList:
+                          List:
+                            ast.Field:
+                              Names:
+                                ast.Ident:
+                                  Name: x
+                              Type:
+                                ast.Ident:
+                                  Name: int
+                  Body:
+                    ast.BlockStmt:
+                      List:
+                        ast.ExprStmt:
+                          X:
+                            ast.Ident:
+                              Name: println
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: call
+              Args:
+                ast.FuncLit:
+                  Type:
+                    ast.FuncType:
+                      Params:
+                        ast.FieldList:
+                          List:
+                            ast.Field:
+                              Names:
+                                ast.Ident:
+                                  Name: x
+                              Type:
+                                ast.Ident:
+                                  Name: int
+                  Body:
+                    ast.BlockStmt:
+                      List:
+                        ast.ExprStmt:
+                          X:
+                            ast.CallExpr:
+                              Fun:
+                                ast.Ident:
+                                  Name: println
+                              Args:
+                                ast.Ident:
+                                  Name: x
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: call
+              Args:
+                ast.FuncLit:
+                  Type:
+                    ast.FuncType:
+                      Params:
+                        ast.FieldList:
+                          List:
+                            ast.Field:
+                              Names:
+                                ast.Ident:
+                                  Name: x
+                              Type:
+                                ast.Ident:
+                                  Name: int
+                  Body:
+                    ast.BlockStmt:
+                      List:
+                        ast.ExprStmt:
+                          X:
+                            ast.CallExpr:
+                              Fun:
+                                ast.Ident:
+                                  Name: println
+                              Args:
+                                ast.CompositeLit:
+                                  Elts:
+                                    ast.KeyValueExpr:
+                                      Key:
+                                        ast.BasicLit:
+                                          Kind: STRING
+                                          Value: "x"
+                                      Value:
+                                        ast.BasicLit:
+                                          Kind: INT
+                                          Value: 100
+                                    ast.KeyValueExpr:
+                                      Key:
+                                        ast.BasicLit:
+                                          Kind: STRING
+                                          Value: "y"
+                                      Value:
+                                        ast.BasicLit:
+                                          Kind: INT
+                                          Value: 200
+                                ast.Ident:
+                                  Name: x
diff --git a/parser/parsertest/memfs.go b/parser/parsertest/memfs.go
index c46cb3a1..dbe7e402 100644
--- a/parser/parsertest/memfs.go
+++ b/parser/parsertest/memfs.go
@@ -17,6 +17,7 @@
 package parsertest
 
 import (
+	"io/fs"
 	"os"
 	"path"
 	"syscall"
@@ -66,9 +67,9 @@ func NewMemFS(dirs map[string][]string, files map[string]string) *MemFS {
 
 // ReadDir reads the directory named by dirname and returns
 // a list of directory entries sorted by filename.
-func (p *MemFS) ReadDir(dirname string) ([]os.FileInfo, error) {
+func (p *MemFS) ReadDir(dirname string) ([]fs.FileInfo, error) {
 	if items, ok := p.dirs[dirname]; ok {
-		fis := make([]os.FileInfo, len(items))
+		fis := make([]fs.FileInfo, len(items))
 		for i, item := range items {
 			fis[i] = &memFileInfo{name: item}
 		}
diff --git a/printer/gop_test.go b/printer/gop_test.go
index cfd198dd..0440e7d9 100644
--- a/printer/gop_test.go
+++ b/printer/gop_test.go
@@ -18,6 +18,7 @@ package printer_test
 
 import (
 	"bytes"
+	"io/fs"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -145,6 +146,12 @@ func testFrom(t *testing.T, fpath, sel string, mode int) {
 	}
 }
 
+func TestFromGopPrinter(t *testing.T) {
+	testFrom(t, "nodes.go", "", 0)
+	testFrom(t, "printer.go", "", 0)
+	testFrom(t, "printer_test.go", "", 0)
+}
+
 func TestFromTestdata(t *testing.T) {
 	sel := ""
 	dir, err := os.Getwd()
@@ -152,7 +159,7 @@ func TestFromTestdata(t *testing.T) {
 		t.Fatal("Getwd failed:", err)
 	}
 	dir = filepath.Join(dir, "./_testdata")
-	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+	filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {
 		if err != nil {
 			return err
 		}
@@ -170,7 +177,7 @@ func TestFromParse(t *testing.T) {
 		t.Fatal("Getwd failed:", err)
 	}
 	dir = filepath.Join(dir, "../parser/_testdata")
-	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+	filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {
 		if err != nil {
 			return err
 		}