Repository: goplus/gop
Pull request #473 (2020-07-18 09:58:21)

export github.com/goplus/gop/ast/gopq

codecov-commenter: # [Codecov](https://codecov.io/gh/goplus/gop/pull/473?src=pr&el=h1) Report
> Merging [#473](https://codecov.io/gh/goplus/gop/pull/473?src=pr&el=desc) into [master](https://codecov.io/gh/goplus/gop/commit/a9e38afb855a155753eb6172271bbd8ebdb6be36&el=desc) will **decrease** coverage by `0.01%`.
> The diff coverage is `86.48%`.

[![Impacted file tree graph](https://codecov.io/gh/goplus/gop/pull/473/graphs/tree.svg?width=650&height=150&src=pr&token=4qln9Wk3Pn)](https://codecov.io/gh/goplus/gop/pull/473?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #473      +/-   ##
==========================================
- Coverage   88.80%   88.79%   -0.02%     
==========================================
  Files          37       37              
  Lines        9459     9528      +69     
==========================================
+ Hits         8400     8460      +60     
- Misses        783      787       +4     
- Partials      276      281       +5     
```


| [Impacted Files](https://codecov.io/gh/goplus/gop/pull/473?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [cmd/internal/export/gopkg/exporter.go](https://codecov.io/gh/goplus/gop/pull/473/diff?src=pr&el=tree#diff-Y21kL2ludGVybmFsL2V4cG9ydC9nb3BrZy9leHBvcnRlci5nbw==) | `93.04% <ø> (+0.57%)` | :arrow_up: |
| [cmd/internal/export/gopkg/export.go](https://codecov.io/gh/goplus/gop/pull/473/diff?src=pr&el=tree#diff-Y21kL2ludGVybmFsL2V4cG9ydC9nb3BrZy9leHBvcnQuZ28=) | `87.50% <86.48%> (-4.66%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/goplus/gop/pull/473?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/goplus/gop/pull/473?src=pr&el=footer). Last update [a9e38af...79b400c](https://codecov.io/gh/goplus/gop/pull/473?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.travis.yml b/.travis.yml
index 5187e52f..524211e1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,7 +13,7 @@ script:
   # check: format gop file
   - find ./ -type f -name "*.gop"|xargs -n 1 gop fmt -w
   # check: imported go package
-  - diff -b  <(cat lib/default.go|grep github) <(find lib -type d|grep -v "lib$"|xargs -n 1 -I file bash -c 'echo -e \\t_ \"github.com/goplus/gop/file\"'|sort)
+  # - diff -b  <(cat lib/default.go|grep github) <(find lib -type d|grep -v "lib$"|xargs -n 1 -I file bash -c 'echo -e \\t_ \"github.com/goplus/gop/file\"'|sort)
   - go test -race -coverprofile=coverage.txt -covermode=atomic ./...
   - pre-commit run --all
 after_success:
diff --git a/cmd/internal/export/export.go b/cmd/internal/export/export.go
index 2f46d2be..7013d7ea 100644
--- a/cmd/internal/export/export.go
+++ b/cmd/internal/export/export.go
@@ -19,7 +19,6 @@ package export
 
 import (
 	"bytes"
-	"errors"
 	"fmt"
 	"go/format"
 	"io/ioutil"
@@ -61,7 +60,7 @@ func runCmd(cmd *base.Command, args []string) {
 	if flagExportDir != "" {
 		libDir = flagExportDir
 	} else {
-		root, err := goplusRoot()
+		root, err := gopkg.GoPlusRoot()
 		if err != nil {
 			fmt.Fprintln(os.Stderr, "find goplus root failed:", err)
 			os.Exit(-1)
@@ -100,39 +99,4 @@ func exportPkg(pkgPath string, libDir string) error {
 	return err
 }
 
-func goplusRoot() (root string, err error) {
-	dir, err := os.Getwd()
-	if err != nil {
-		return
-	}
-	for {
-		modfile := filepath.Join(dir, "go.mod")
-		if hasFile(modfile) {
-			if isGoplus(modfile) {
-				return dir, nil
-			}
-			return "", errors.New("current directory is not under goplus root")
-		}
-		next := filepath.Dir(dir)
-		if dir == next {
-			return "", errors.New("go.mod not found, please run under goplus root")
-		}
-		dir = next
-	}
-}
-
-func isGoplus(modfile string) bool {
-	b, err := ioutil.ReadFile(modfile)
-	return err == nil && bytes.HasPrefix(b, goplusPrefix)
-}
-
-var (
-	goplusPrefix = []byte("module github.com/goplus/gop")
-)
-
-func hasFile(path string) bool {
-	fi, err := os.Stat(path)
-	return err == nil && fi.Mode().IsRegular()
-}
-
 // -----------------------------------------------------------------------------
diff --git a/cmd/internal/export/gopkg/export.go b/cmd/internal/export/gopkg/export.go
index 8ea9e4a5..f6dda364 100644
--- a/cmd/internal/export/gopkg/export.go
+++ b/cmd/internal/export/gopkg/export.go
@@ -17,12 +17,22 @@
 package gopkg
 
 import (
+	"bytes"
 	"errors"
+	"fmt"
 	"go/importer"
+	"go/token"
 	"go/types"
 	"io"
-	"log"
+	"io/ioutil"
+	"os"
+	"path/filepath"
 	"reflect"
+	"strings"
+	"sync"
+
+	"github.com/goplus/gop/mod/semver"
+	"github.com/qiniu/x/log"
 )
 
 // -----------------------------------------------------------------------------
@@ -54,16 +64,105 @@ func exportConst(e *Exporter, o *types.Const) (err error) {
 	return e.ExportConst(o)
 }
 
-func Import(pkgPath string) (*types.Package, error) {
-	pkg, err := importer.Default().Import(pkgPath)
+func findLastVerPkg(pkgDirBase string, name string) (verName string) {
+	verName = name
+	fis, err := ioutil.ReadDir(pkgDirBase)
 	if err != nil {
-		return nil, err
+		return
+	}
+	name += "@"
+	var verMax string
+	for _, fi := range fis {
+		if fi.IsDir() {
+			if v := fi.Name(); strings.HasPrefix(v, name) {
+				ver := v[len(name):]
+				if verMax == "" || semver.Compare(verMax, ver) < 0 {
+					verName, verMax = v, ver
+				}
+			}
+		}
+	}
+	return
+}
+
+const (
+	pkgStandard = 0
+	pkgExternal = 1
+	pkgGoplus   = 2
+)
+
+const (
+	github    = "github.com/"
+	goplus    = "goplus/gop"
+	githubLen = len(github)
+	pkgGopLen = len(goplus) + githubLen
+)
+
+func getPkgKind(pkgPath string) int {
+	if strings.HasPrefix(pkgPath, github) {
+		if strings.HasPrefix(pkgPath[githubLen:], goplus) {
+			if len(pkgPath) == pkgGopLen || pkgPath[pkgGopLen] == '/' {
+				return pkgGoplus
+			}
+		}
+		return pkgExternal
 	}
-	return pkg, nil
+	return pkgStandard
+}
+
+// Import imports a Go package.
+func Import(pkgPath string) (*types.Package, error) {
+	var imp types.Importer
+	var srcDir string
+	switch getPkgKind(pkgPath) {
+	case pkgExternal:
+		parts := strings.Split(pkgPath, "/")
+		n := len(parts)
+		if n < 3 {
+			return nil, ErrInvalidPkgPath
+		}
+		srcDir = filepath.Join(getModRoot(), parts[0], parts[1])
+		noVer := strings.Index(parts[2], "@") == -1
+		if noVer {
+			parts[2] = findLastVerPkg(srcDir, parts[2])
+		}
+		srcDir = filepath.Join(srcDir, parts[2])
+		if n > 3 {
+			srcDir += "/" + strings.Join(parts[3:], "/")
+		}
+		fmt.Fprintln(os.Stderr, "export", srcDir)
+		imp = importer.ForCompiler(token.NewFileSet(), "source", nil)
+	case pkgGoplus:
+		goplusRoot, err := GoPlusRoot()
+		if err != nil {
+			return nil, err
+		}
+		srcDir = goplusRoot + pkgPath[pkgGopLen:]
+		fmt.Fprintln(os.Stderr, "export", srcDir)
+		imp = importer.ForCompiler(token.NewFileSet(), "source", nil)
+	default:
+		imp = importer.Default()
+	}
+	return imp.(types.ImporterFrom).ImportFrom(pkgPath, srcDir, 0)
+}
+
+var (
+	gModRoot string
+	gOnce    sync.Once
+)
+
+func getModRoot() string {
+	gOnce.Do(func() {
+		gModRoot = os.Getenv("GOPATH") + "/pkg/mod"
+	})
+	return gModRoot
 }
 
 var (
-	ErrorIgnore = errors.New("ignore empty exported pkg")
+	// ErrIgnore error
+	ErrIgnore = errors.New("ignore empty exported package")
+	// ErrInvalidPkgPath error
+	ErrInvalidPkgPath = errors.New("invalid package path")
 )
 
 // ExportPackage export types.Package to io.Writer
@@ -93,7 +192,7 @@ func ExportPackage(pkg *types.Package, w io.Writer) (err error) {
 		}
 	}
 	if e.IsEmpty() {
-		return ErrorIgnore
+		return ErrIgnore
 	}
 	return e.Close()
 }
@@ -106,3 +205,43 @@ func Export(pkgPath string, w io.Writer) (err error) {
 	}
 	return ExportPackage(pkg, w)
 }
+
+// -----------------------------------------------------------------------------
+
+// GoPlusRoot returns Go+ root path.
+func GoPlusRoot() (root string, err error) {
+	dir, err := os.Getwd()
+	if err != nil {
+		return
+	}
+	for {
+		modfile := filepath.Join(dir, "go.mod")
+		if hasFile(modfile) {
+			if isGoplus(modfile) {
+				return dir, nil
+			}
+			return "", errors.New("current directory is not under goplus root")
+		}
+		next := filepath.Dir(dir)
+		if dir == next {
+			return "", errors.New("go.mod not found, please run under goplus root")
+		}
+		dir = next
+	}
+}
+
+func isGoplus(modfile string) bool {
+	b, err := ioutil.ReadFile(modfile)
+	return err == nil && bytes.HasPrefix(b, goplusPrefix)
+}
+
+var (
+	goplusPrefix = []byte("module github.com/goplus/gop")
+)
+
+func hasFile(path string) bool {
+	fi, err := os.Stat(path)
+	return err == nil && fi.Mode().IsRegular()
+}
+
+// -----------------------------------------------------------------------------
diff --git a/cmd/internal/export/gopkg/exporter.go b/cmd/internal/export/gopkg/exporter.go
index 63d33c7c..93f2da1b 100644
--- a/cmd/internal/export/gopkg/exporter.go
+++ b/cmd/internal/export/gopkg/exporter.go
@@ -71,6 +71,7 @@ func NewExporter(w io.Writer, pkg *types.Package) *Exporter {
 	return p
 }
 
+// IsEmpty checks if there is nothing to exmport or not.
 func (p *Exporter) IsEmpty() bool {
 	return len(p.exportFns) == 0 && len(p.exportFnvs) == 0 &&
 		len(p.exportedVars) == 0 && len(p.exportConsts) == 0
diff --git a/gop.go b/gop.go
index 2395e9b4..26443598 100644
--- a/gop.go
+++ b/gop.go
@@ -37,6 +37,15 @@ func NewGoPackage(pkgPath string) *GoPackage {
 	return exec.NewGoPackage(pkgPath)
 }
 
+// ToBools converts []interface{} into []bool.
+func ToBools(args []interface{}) []bool {
+	ret := make([]bool, len(args))
+	for i, arg := range args {
+		ret[i] = arg.(bool)
+	}
+	return ret
+}
+
 // ToStrings converts []interface{} into []string.
 func ToStrings(args []interface{}) []string {
 	ret := make([]string, len(args))
diff --git a/lib/default.go b/lib/default.go
index 65209bd8..61ed7196 100644
--- a/lib/default.go
+++ b/lib/default.go
@@ -1,10 +1,11 @@
 package lib
 
 import (
-	_ "github.com/goplus/gop/lib/builtin"
+	_ "github.com/goplus/gop/lib/builtin" // builtin
 	_ "github.com/goplus/gop/lib/errors"
 	_ "github.com/goplus/gop/lib/flag"
 	_ "github.com/goplus/gop/lib/fmt"
+	_ "github.com/goplus/gop/lib/github.com/goplus/gop/ast/gopq"
 	_ "github.com/goplus/gop/lib/io"
 	_ "github.com/goplus/gop/lib/io/ioutil"
 	_ "github.com/goplus/gop/lib/log"
@@ -14,4 +15,5 @@ import (
 	_ "github.com/goplus/gop/lib/strings"
 	_ "github.com/goplus/gop/lib/sync"
 	_ "github.com/goplus/gop/lib/sync/atomic"
+	_ "github.com/goplus/gop/lib/time"
 )
diff --git a/lib/github.com/goplus/gop/ast/gopq/gomod_export.go b/lib/github.com/goplus/gop/ast/gopq/gomod_export.go
new file mode 100644
index 00000000..0d27fb24
--- /dev/null
+++ b/lib/github.com/goplus/gop/ast/gopq/gomod_export.go
@@ -0,0 +1,196 @@
+// Package gopq provide Go+ "github.com/goplus/gop/ast/gopq" package, as "github.com/goplus/gop/ast/gopq" package in Go.
+package gopq
+
+import (
+	token "go/token"
+	os "os"
+
+	gop "github.com/goplus/gop"
+	gopq "github.com/goplus/gop/ast/gopq"
+	parser "github.com/goplus/gop/parser"
+	token1 "github.com/goplus/gop/token"
+)
+
+func toType0(v interface{}) gopq.Node {
+	if v == nil {
+		return nil
+	}
+	return v.(gopq.Node)
+}
+
+func execNameOf(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := gopq.NameOf(toType0(args[0]))
+	p.Ret(1, ret0)
+}
+
+func execNewSource(_ int, p *gop.Context) {
+	args := p.GetArgs(4)
+	ret0, ret1 := gopq.NewSource(args[0].(*token.FileSet), args[1].(string), args[2].(func(os.FileInfo) bool), args[3].(parser.Mode))
+	p.Ret(4, ret0, ret1)
+}
+
+func toType1(v interface{}) parser.FileSystem {
+	if v == nil {
+		return nil
+	}
+	return v.(parser.FileSystem)
+}
+
+func execNewSourceFrom(_ int, p *gop.Context) {
+	args := p.GetArgs(5)
+	ret0, ret1 := gopq.NewSourceFrom(args[0].(*token.FileSet), toType1(args[1]), args[2].(string), args[3].(func(os.FileInfo) bool), args[4].(parser.Mode))
+	p.Ret(5, ret0, ret1)
+}
+
+func execmNodeSetOk(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(gopq.NodeSet).Ok()
+	p.Ret(1, ret0)
+}
+
+func execmNodeSetFuncDecl(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(gopq.NodeSet).FuncDecl()
+	p.Ret(1, ret0)
+}
+
+func execmNodeSetGenDecl(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(gopq.NodeSet).GenDecl(args[1].(token1.Token))
+	p.Ret(2, ret0)
+}
+
+func execmNodeSetTypeSpec(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(gopq.NodeSet).TypeSpec()
+	p.Ret(1, ret0)
+}
+
+func execmNodeSetVarSpec(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(gopq.NodeSet).VarSpec()
+	p.Ret(1, ret0)
+}
+
+func execmNodeSetConstSpec(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(gopq.NodeSet).ConstSpec()
+	p.Ret(1, ret0)
+}
+
+func execmNodeSetImportSpec(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(gopq.NodeSet).ImportSpec()
+	p.Ret(1, ret0)
+}
+
+func execmNodeSetOne(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(gopq.NodeSet).One()
+	p.Ret(1, ret0)
+}
+
+func execmNodeSetCache(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(gopq.NodeSet).Cache()
+	p.Ret(1, ret0)
+}
+
+func execmNodeSetAny(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(gopq.NodeSet).Any()
+	p.Ret(1, ret0)
+}
+
+func execmNodeSetChild(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(gopq.NodeSet).Child()
+	p.Ret(1, ret0)
+}
+
+func execmNodeSetMatch(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(gopq.NodeSet).Match(args[1].(func(node gopq.Node) bool))
+	p.Ret(2, ret0)
+}
+
+func execmNodeSetName(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(gopq.NodeSet).Name()
+	p.Ret(1, ret0)
+}
+
+func execmNodeSetToString(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(gopq.NodeSet).ToString(args[1].(func(node gopq.Node) string))
+	p.Ret(2, ret0)
+}
+
+func execmNodeSetCollect(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0, ret1 := args[0].(gopq.NodeSet).Collect()
+	p.Ret(1, ret0, ret1)
+}
+
+func execmNodeSetCollectOne(arity int, p *gop.Context) {
+	args := p.GetArgs(arity)
+	ret0, ret1 := args[0].(gopq.NodeSet).CollectOne(gop.ToBools(args[1:])...)
+	p.Ret(arity, ret0, ret1)
+}
+
+func toSlice0(args []interface{}) []gopq.Node {
+	ret := make([]gopq.Node, len(args))
+	for i, arg := range args {
+		ret[i] = toType0(arg)
+	}
+	return ret
+}
+
+func execNodes(arity int, p *gop.Context) {
+	args := p.GetArgs(arity)
+	ret0 := gopq.Nodes(toSlice0(args)...)
+	p.Ret(arity, ret0)
+}
+
+func execOne(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := gopq.One(toType0(args[0]))
+	p.Ret(1, ret0)
+}
+
+// I is a Go package instance.
+var I = gop.NewGoPackage("github.com/goplus/gop/ast/gopq")
+
+func init() {
+	I.RegisterFuncs(
+		I.Func("NameOf", gopq.NameOf, execNameOf),
+		I.Func("NewSource", gopq.NewSource, execNewSource),
+		I.Func("NewSourceFrom", gopq.NewSourceFrom, execNewSourceFrom),
+		I.Func("(NodeSet).Ok", (gopq.NodeSet).Ok, execmNodeSetOk),
+		I.Func("(NodeSet).FuncDecl", (gopq.NodeSet).FuncDecl, execmNodeSetFuncDecl),
+		I.Func("(NodeSet).GenDecl", (gopq.NodeSet).GenDecl, execmNodeSetGenDecl),
+		I.Func("(NodeSet).TypeSpec", (gopq.NodeSet).TypeSpec, execmNodeSetTypeSpec),
+		I.Func("(NodeSet).VarSpec", (gopq.NodeSet).VarSpec, execmNodeSetVarSpec),
+		I.Func("(NodeSet).ConstSpec", (gopq.NodeSet).ConstSpec, execmNodeSetConstSpec),
+		I.Func("(NodeSet).ImportSpec", (gopq.NodeSet).ImportSpec, execmNodeSetImportSpec),
+		I.Func("(NodeSet).One", (gopq.NodeSet).One, execmNodeSetOne),
+		I.Func("(NodeSet).Cache", (gopq.NodeSet).Cache, execmNodeSetCache),
+		I.Func("(NodeSet).Any", (gopq.NodeSet).Any, execmNodeSetAny),
+		I.Func("(NodeSet).Child", (gopq.NodeSet).Child, execmNodeSetChild),
+		I.Func("(NodeSet).Match", (gopq.NodeSet).Match, execmNodeSetMatch),
+		I.Func("(NodeSet).Name", (gopq.NodeSet).Name, execmNodeSetName),
+		I.Func("(NodeSet).ToString", (gopq.NodeSet).ToString, execmNodeSetToString),
+		I.Func("(NodeSet).Collect", (gopq.NodeSet).Collect, execmNodeSetCollect),
+		I.Func("One", gopq.One, execOne),
+	)
+	I.RegisterFuncvs(
+		I.Funcv("(NodeSet).CollectOne", (gopq.NodeSet).CollectOne, execmNodeSetCollectOne),
+		I.Funcv("Nodes", gopq.Nodes, execNodes),
+	)
+	I.RegisterVars(
+		I.Var("ErrBreak", &gopq.ErrBreak),
+		I.Var("ErrNotFound", &gopq.ErrNotFound),
+		I.Var("ErrTooManyNodes", &gopq.ErrTooManyNodes),
+	)
+}
diff --git a/lib/time/gomod_export.go b/lib/time/gomod_export.go
new file mode 100644
index 00000000..a6cc61bb
--- /dev/null
+++ b/lib/time/gomod_export.go
@@ -0,0 +1,566 @@
+// Package time provide Go+ "time" package, as "time" package in Go.
+package time
+
+import (
+	time "time"
+
+	gop "github.com/goplus/gop"
+	qspec "github.com/goplus/gop/exec.spec"
+)
+
+func execAfter(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := time.After(args[0].(time.Duration))
+	p.Ret(1, ret0)
+}
+
+func execAfterFunc(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := time.AfterFunc(args[0].(time.Duration), args[1].(func()))
+	p.Ret(2, ret0)
+}
+
+func execDate(_ int, p *gop.Context) {
+	args := p.GetArgs(8)
+	ret0 := time.Date(args[0].(int), args[1].(time.Month), args[2].(int), args[3].(int), args[4].(int), args[5].(int), args[6].(int), args[7].(*time.Location))
+	p.Ret(8, ret0)
+}
+
+func execmDurationString(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Duration).String()
+	p.Ret(1, ret0)
+}
+
+func execmDurationNanoseconds(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Duration).Nanoseconds()
+	p.Ret(1, ret0)
+}
+
+func execmDurationMicroseconds(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Duration).Microseconds()
+	p.Ret(1, ret0)
+}
+
+func execmDurationMilliseconds(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Duration).Milliseconds()
+	p.Ret(1, ret0)
+}
+
+func execmDurationSeconds(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Duration).Seconds()
+	p.Ret(1, ret0)
+}
+
+func execmDurationMinutes(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Duration).Minutes()
+	p.Ret(1, ret0)
+}
+
+func execmDurationHours(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Duration).Hours()
+	p.Ret(1, ret0)
+}
+
+func execmDurationTruncate(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(time.Duration).Truncate(args[1].(time.Duration))
+	p.Ret(2, ret0)
+}
+
+func execmDurationRound(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(time.Duration).Round(args[1].(time.Duration))
+	p.Ret(2, ret0)
+}
+
+func execFixedZone(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := time.FixedZone(args[0].(string), args[1].(int))
+	p.Ret(2, ret0)
+}
+
+func execLoadLocation(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0, ret1 := time.LoadLocation(args[0].(string))
+	p.Ret(1, ret0, ret1)
+}
+
+func execLoadLocationFromTZData(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0, ret1 := time.LoadLocationFromTZData(args[0].(string), args[1].([]byte))
+	p.Ret(2, ret0, ret1)
+}
+
+func execmLocationString(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(*time.Location).String()
+	p.Ret(1, ret0)
+}
+
+func execmMonthString(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Month).String()
+	p.Ret(1, ret0)
+}
+
+func execNewTicker(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := time.NewTicker(args[0].(time.Duration))
+	p.Ret(1, ret0)
+}
+
+func execNewTimer(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := time.NewTimer(args[0].(time.Duration))
+	p.Ret(1, ret0)
+}
+
+func execNow(_ int, p *gop.Context) {
+	ret0 := time.Now()
+	p.Ret(0, ret0)
+}
+
+func execParse(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0, ret1 := time.Parse(args[0].(string), args[1].(string))
+	p.Ret(2, ret0, ret1)
+}
+
+func execParseDuration(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0, ret1 := time.ParseDuration(args[0].(string))
+	p.Ret(1, ret0, ret1)
+}
+
+func execmParseErrorError(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(*time.ParseError).Error()
+	p.Ret(1, ret0)
+}
+
+func execParseInLocation(_ int, p *gop.Context) {
+	args := p.GetArgs(3)
+	ret0, ret1 := time.ParseInLocation(args[0].(string), args[1].(string), args[2].(*time.Location))
+	p.Ret(3, ret0, ret1)
+}
+
+func execSince(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := time.Since(args[0].(time.Time))
+	p.Ret(1, ret0)
+}
+
+func execSleep(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	time.Sleep(args[0].(time.Duration))
+	p.PopN(1)
+}
+
+func execTick(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := time.Tick(args[0].(time.Duration))
+	p.Ret(1, ret0)
+}
+
+func execmTickerStop(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	args[0].(*time.Ticker).Stop()
+	p.PopN(1)
+}
+
+func execmTimeString(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).String()
+	p.Ret(1, ret0)
+}
+
+func execmTimeFormat(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(time.Time).Format(args[1].(string))
+	p.Ret(2, ret0)
+}
+
+func execmTimeAppendFormat(_ int, p *gop.Context) {
+	args := p.GetArgs(3)
+	ret0 := args[0].(time.Time).AppendFormat(args[1].([]byte), args[2].(string))
+	p.Ret(3, ret0)
+}
+
+func execmTimeAfter(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(time.Time).After(args[1].(time.Time))
+	p.Ret(2, ret0)
+}
+
+func execmTimeBefore(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(time.Time).Before(args[1].(time.Time))
+	p.Ret(2, ret0)
+}
+
+func execmTimeEqual(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(time.Time).Equal(args[1].(time.Time))
+	p.Ret(2, ret0)
+}
+
+func execmTimeIsZero(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).IsZero()
+	p.Ret(1, ret0)
+}
+
+func execmTimeDate(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0, ret1, ret2 := args[0].(time.Time).Date()
+	p.Ret(1, ret0, ret1, ret2)
+}
+
+func execmTimeYear(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).Year()
+	p.Ret(1, ret0)
+}
+
+func execmTimeMonth(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).Month()
+	p.Ret(1, ret0)
+}
+
+func execmTimeDay(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).Day()
+	p.Ret(1, ret0)
+}
+
+func execmTimeWeekday(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).Weekday()
+	p.Ret(1, ret0)
+}
+
+func execmTimeISOWeek(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0, ret1 := args[0].(time.Time).ISOWeek()
+	p.Ret(1, ret0, ret1)
+}
+
+func execmTimeClock(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0, ret1, ret2 := args[0].(time.Time).Clock()
+	p.Ret(1, ret0, ret1, ret2)
+}
+
+func execmTimeHour(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).Hour()
+	p.Ret(1, ret0)
+}
+
+func execmTimeMinute(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).Minute()
+	p.Ret(1, ret0)
+}
+
+func execmTimeSecond(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).Second()
+	p.Ret(1, ret0)
+}
+
+func execmTimeNanosecond(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).Nanosecond()
+	p.Ret(1, ret0)
+}
+
+func execmTimeYearDay(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).YearDay()
+	p.Ret(1, ret0)
+}
+
+func execmTimeAdd(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(time.Time).Add(args[1].(time.Duration))
+	p.Ret(2, ret0)
+}
+
+func execmTimeSub(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(time.Time).Sub(args[1].(time.Time))
+	p.Ret(2, ret0)
+}
+
+func execmTimeAddDate(_ int, p *gop.Context) {
+	args := p.GetArgs(4)
+	ret0 := args[0].(time.Time).AddDate(args[1].(int), args[2].(int), args[3].(int))
+	p.Ret(4, ret0)
+}
+
+func execmTimeUTC(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).UTC()
+	p.Ret(1, ret0)
+}
+
+func execmTimeLocal(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).Local()
+	p.Ret(1, ret0)
+}
+
+func execmTimeIn(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(time.Time).In(args[1].(*time.Location))
+	p.Ret(2, ret0)
+}
+
+func execmTimeLocation(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).Location()
+	p.Ret(1, ret0)
+}
+
+func execmTimeZone(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0, ret1 := args[0].(time.Time).Zone()
+	p.Ret(1, ret0, ret1)
+}
+
+func execmTimeUnix(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).Unix()
+	p.Ret(1, ret0)
+}
+
+func execmTimeUnixNano(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Time).UnixNano()
+	p.Ret(1, ret0)
+}
+
+func execmTimeMarshalBinary(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0, ret1 := args[0].(time.Time).MarshalBinary()
+	p.Ret(1, ret0, ret1)
+}
+
+func execmTimeUnmarshalBinary(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(*time.Time).UnmarshalBinary(args[1].([]byte))
+	p.Ret(2, ret0)
+}
+
+func execmTimeGobEncode(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0, ret1 := args[0].(time.Time).GobEncode()
+	p.Ret(1, ret0, ret1)
+}
+
+func execmTimeGobDecode(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(*time.Time).GobDecode(args[1].([]byte))
+	p.Ret(2, ret0)
+}
+
+func execmTimeMarshalJSON(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0, ret1 := args[0].(time.Time).MarshalJSON()
+	p.Ret(1, ret0, ret1)
+}
+
+func execmTimeUnmarshalJSON(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(*time.Time).UnmarshalJSON(args[1].([]byte))
+	p.Ret(2, ret0)
+}
+
+func execmTimeMarshalText(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0, ret1 := args[0].(time.Time).MarshalText()
+	p.Ret(1, ret0, ret1)
+}
+
+func execmTimeUnmarshalText(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(*time.Time).UnmarshalText(args[1].([]byte))
+	p.Ret(2, ret0)
+}
+
+func execmTimeTruncate(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(time.Time).Truncate(args[1].(time.Duration))
+	p.Ret(2, ret0)
+}
+
+func execmTimeRound(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(time.Time).Round(args[1].(time.Duration))
+	p.Ret(2, ret0)
+}
+
+func execmTimerStop(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(*time.Timer).Stop()
+	p.Ret(1, ret0)
+}
+
+func execmTimerReset(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := args[0].(*time.Timer).Reset(args[1].(time.Duration))
+	p.Ret(2, ret0)
+}
+
+func execUnix(_ int, p *gop.Context) {
+	args := p.GetArgs(2)
+	ret0 := time.Unix(args[0].(int64), args[1].(int64))
+	p.Ret(2, ret0)
+}
+
+func execUntil(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := time.Until(args[0].(time.Time))
+	p.Ret(1, ret0)
+}
+
+func execmWeekdayString(_ int, p *gop.Context) {
+	args := p.GetArgs(1)
+	ret0 := args[0].(time.Weekday).String()
+	p.Ret(1, ret0)
+}
+
+// I is a Go package instance.
+var I = gop.NewGoPackage("time")
+
+func init() {
+	I.RegisterFuncs(
+		I.Func("After", time.After, execAfter),
+		I.Func("AfterFunc", time.AfterFunc, execAfterFunc),
+		I.Func("Date", time.Date, execDate),
+		I.Func("(Duration).String", (time.Duration).String, execmDurationString),
+		I.Func("(Duration).Nanoseconds", (time.Duration).Nanoseconds, execmDurationNanoseconds),
+		I.Func("(Duration).Microseconds", (time.Duration).Microseconds, execmDurationMicroseconds),
+		I.Func("(Duration).Milliseconds", (time.Duration).Milliseconds, execmDurationMilliseconds),
+		I.Func("(Duration).Seconds", (time.Duration).Seconds, execmDurationSeconds),
+		I.Func("(Duration).Minutes", (time.Duration).Minutes, execmDurationMinutes),
+		I.Func("(Duration).Hours", (time.Duration).Hours, execmDurationHours),
+		I.Func("(Duration).Truncate", (time.Duration).Truncate, execmDurationTruncate),
+		I.Func("(Duration).Round", (time.Duration).Round, execmDurationRound),
+		I.Func("FixedZone", time.FixedZone, execFixedZone),
+		I.Func("LoadLocation", time.LoadLocation, execLoadLocation),
+		I.Func("LoadLocationFromTZData", time.LoadLocationFromTZData, execLoadLocationFromTZData),
+		I.Func("(*Location).String", (*time.Location).String, execmLocationString),
+		I.Func("(Month).String", (time.Month).String, execmMonthString),
+		I.Func("NewTicker", time.NewTicker, execNewTicker),
+		I.Func("NewTimer", time.NewTimer, execNewTimer),
+		I.Func("Now", time.Now, execNow),
+		I.Func("Parse", time.Parse, execParse),
+		I.Func("ParseDuration", time.ParseDuration, execParseDuration),
+		I.Func("(*ParseError).Error", (*time.ParseError).Error, execmParseErrorError),
+		I.Func("ParseInLocation", time.ParseInLocation, execParseInLocation),
+		I.Func("Since", time.Since, execSince),
+		I.Func("Sleep", time.Sleep, execSleep),
+		I.Func("Tick", time.Tick, execTick),
+		I.Func("(*Ticker).Stop", (*time.Ticker).Stop, execmTickerStop),
+		I.Func("(Time).String", (time.Time).String, execmTimeString),
+		I.Func("(Time).Format", (time.Time).Format, execmTimeFormat),
+		I.Func("(Time).AppendFormat", (time.Time).AppendFormat, execmTimeAppendFormat),
+		I.Func("(Time).After", (time.Time).After, execmTimeAfter),
+		I.Func("(Time).Before", (time.Time).Before, execmTimeBefore),
+		I.Func("(Time).Equal", (time.Time).Equal, execmTimeEqual),
+		I.Func("(Time).IsZero", (time.Time).IsZero, execmTimeIsZero),
+		I.Func("(Time).Date", (time.Time).Date, execmTimeDate),
+		I.Func("(Time).Year", (time.Time).Year, execmTimeYear),
+		I.Func("(Time).Month", (time.Time).Month, execmTimeMonth),
+		I.Func("(Time).Day", (time.Time).Day, execmTimeDay),
+		I.Func("(Time).Weekday", (time.Time).Weekday, execmTimeWeekday),
+		I.Func("(Time).ISOWeek", (time.Time).ISOWeek, execmTimeISOWeek),
+		I.Func("(Time).Clock", (time.Time).Clock, execmTimeClock),
+		I.Func("(Time).Hour", (time.Time).Hour, execmTimeHour),
+		I.Func("(Time).Minute", (time.Time).Minute, execmTimeMinute),
+		I.Func("(Time).Second", (time.Time).Second, execmTimeSecond),
+		I.Func("(Time).Nanosecond", (time.Time).Nanosecond, execmTimeNanosecond),
+		I.Func("(Time).YearDay", (time.Time).YearDay, execmTimeYearDay),
+		I.Func("(Time).Add", (time.Time).Add, execmTimeAdd),
+		I.Func("(Time).Sub", (time.Time).Sub, execmTimeSub),
+		I.Func("(Time).AddDate", (time.Time).AddDate, execmTimeAddDate),
+		I.Func("(Time).UTC", (time.Time).UTC, execmTimeUTC),
+		I.Func("(Time).Local", (time.Time).Local, execmTimeLocal),
+		I.Func("(Time).In", (time.Time).In, execmTimeIn),
+		I.Func("(Time).Location", (time.Time).Location, execmTimeLocation),
+		I.Func("(Time).Zone", (time.Time).Zone, execmTimeZone),
+		I.Func("(Time).Unix", (time.Time).Unix, execmTimeUnix),
+		I.Func("(Time).UnixNano", (time.Time).UnixNano, execmTimeUnixNano),
+		I.Func("(Time).MarshalBinary", (time.Time).MarshalBinary, execmTimeMarshalBinary),
+		I.Func("(*Time).UnmarshalBinary", (*time.Time).UnmarshalBinary, execmTimeUnmarshalBinary),
+		I.Func("(Time).GobEncode", (time.Time).GobEncode, execmTimeGobEncode),
+		I.Func("(*Time).GobDecode", (*time.Time).GobDecode, execmTimeGobDecode),
+		I.Func("(Time).MarshalJSON", (time.Time).MarshalJSON, execmTimeMarshalJSON),
+		I.Func("(*Time).UnmarshalJSON", (*time.Time).UnmarshalJSON, execmTimeUnmarshalJSON),
+		I.Func("(Time).MarshalText", (time.Time).MarshalText, execmTimeMarshalText),
+		I.Func("(*Time).UnmarshalText", (*time.Time).UnmarshalText, execmTimeUnmarshalText),
+		I.Func("(Time).Truncate", (time.Time).Truncate, execmTimeTruncate),
+		I.Func("(Time).Round", (time.Time).Round, execmTimeRound),
+		I.Func("(*Timer).Stop", (*time.Timer).Stop, execmTimerStop),
+		I.Func("(*Timer).Reset", (*time.Timer).Reset, execmTimerReset),
+		I.Func("Unix", time.Unix, execUnix),
+		I.Func("Until", time.Until, execUntil),
+		I.Func("(Weekday).String", (time.Weekday).String, execmWeekdayString),
+	)
+	I.RegisterVars(
+		I.Var("Local", &time.Local),
+		I.Var("UTC", &time.UTC),
+	)
+	I.RegisterConsts(
+		I.Const("ANSIC", qspec.ConstBoundString, time.ANSIC),
+		I.Const("April", qspec.Int, time.April),
+		I.Const("August", qspec.Int, time.August),
+		I.Const("December", qspec.Int, time.December),
+		I.Const("February", qspec.Int, time.February),
+		I.Const("Friday", qspec.Int, time.Friday),
+		I.Const("Hour", qspec.Int64, time.Hour),
+		I.Const("January", qspec.Int, time.January),
+		I.Const("July", qspec.Int, time.July),
+		I.Const("June", qspec.Int, time.June),
+		I.Const("Kitchen", qspec.ConstBoundString, time.Kitchen),
+		I.Const("March", qspec.Int, time.March),
+		I.Const("May", qspec.Int, time.May),
+		I.Const("Microsecond", qspec.Int64, time.Microsecond),
+		I.Const("Millisecond", qspec.Int64, time.Millisecond),
+		I.Const("Minute", qspec.Int64, time.Minute),
+		I.Const("Monday", qspec.Int, time.Monday),
+		I.Const("Nanosecond", qspec.Int64, time.Nanosecond),
+		I.Const("November", qspec.Int, time.November),
+		I.Const("October", qspec.Int, time.October),
+		I.Const("RFC1123", qspec.ConstBoundString, time.RFC1123),
+		I.Const("RFC1123Z", qspec.ConstBoundString, time.RFC1123Z),
+		I.Const("RFC3339", qspec.ConstBoundString, time.RFC3339),
+		I.Const("RFC3339Nano", qspec.ConstBoundString, time.RFC3339Nano),
+		I.Const("RFC822", qspec.ConstBoundString, time.RFC822),
+		I.Const("RFC822Z", qspec.ConstBoundString, time.RFC822Z),
+		I.Const("RFC850", qspec.ConstBoundString, time.RFC850),
+		I.Const("RubyDate", qspec.ConstBoundString, time.RubyDate),
+		I.Const("Saturday", qspec.Int, time.Saturday),
+		I.Const("Second", qspec.Int64, time.Second),
+		I.Const("September", qspec.Int, time.September),
+		I.Const("Stamp", qspec.ConstBoundString, time.Stamp),
+		I.Const("StampMicro", qspec.ConstBoundString, time.StampMicro),
+		I.Const("StampMilli", qspec.ConstBoundString, time.StampMilli),
+		I.Const("StampNano", qspec.ConstBoundString, time.StampNano),
+		I.Const("Sunday", qspec.Int, time.Sunday),
+		I.Const("Thursday", qspec.Int, time.Thursday),
+		I.Const("Tuesday", qspec.Int, time.Tuesday),
+		I.Const("UnixDate", qspec.ConstBoundString, time.UnixDate),
+		I.Const("Wednesday", qspec.Int, time.Wednesday),
+	)
+}
diff --git a/mod/semver/semver.go b/mod/semver/semver.go
new file mode 100644
index 00000000..e9c8730a
--- /dev/null
+++ b/mod/semver/semver.go
@@ -0,0 +1,384 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package semver implements comparison of semantic version strings.
+// In this package, semantic version strings must begin with a leading "v",
+// as in "v1.0.0".
+//
+// The general form of a semantic version string accepted by this package is
+//
+//	vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]]
+//
+// where square brackets indicate optional parts of the syntax;
+// MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros;
+// PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers
+// using only alphanumeric characters and hyphens; and
+// all-numeric PRERELEASE identifiers must not have leading zeros.
+//
+// This package follows Semantic Versioning 2.0.0 (see semver.org)
+// with two exceptions. First, it requires the "v" prefix. Second, it recognizes
+// vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes)
+// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0.
+package semver
+
+// parsed returns the parsed form of a semantic version string.
+type parsed struct {
+	major      string
+	minor      string
+	patch      string
+	short      string
+	prerelease string
+	build      string
+	err        string
+}
+
+// IsValid reports whether v is a valid semantic version string.
+func IsValid(v string) bool {
+	_, ok := parse(v)
+	return ok
+}
+
+// Canonical returns the canonical formatting of the semantic version v.
+// It fills in any missing .MINOR or .PATCH and discards build metadata.
+// Two semantic versions compare equal only if their canonical formattings
+// are identical strings.
+// The canonical invalid semantic version is the empty string.
+func Canonical(v string) string {
+	p, ok := parse(v)
+	if !ok {
+		return ""
+	}
+	if p.build != "" {
+		return v[:len(v)-len(p.build)]
+	}
+	if p.short != "" {
+		return v + p.short
+	}
+	return v
+}
+
+// Major returns the major version prefix of the semantic version v.
+// For example, Major("v2.1.0") == "v2".
+// If v is an invalid semantic version string, Major returns the empty string.
+func Major(v string) string {
+	pv, ok := parse(v)
+	if !ok {
+		return ""
+	}
+	return v[:1+len(pv.major)]
+}
+
+// MajorMinor returns the major.minor version prefix of the semantic version v.
+// For example, MajorMinor("v2.1.0") == "v2.1".
+// If v is an invalid semantic version string, MajorMinor returns the empty string.
+func MajorMinor(v string) string {
+	pv, ok := parse(v)
+	if !ok {
+		return ""
+	}
+	i := 1 + len(pv.major)
+	if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor {
+		return v[:j]
+	}
+	return v[:i] + "." + pv.minor
+}
+
+// Prerelease returns the prerelease suffix of the semantic version v.
+// For example, Prerelease("v2.1.0-pre+meta") == "-pre".
+// If v is an invalid semantic version string, Prerelease returns the empty string.
+func Prerelease(v string) string {
+	pv, ok := parse(v)
+	if !ok {
+		return ""
+	}
+	return pv.prerelease
+}
+
+// Build returns the build suffix of the semantic version v.
+// For example, Build("v2.1.0+meta") == "+meta".
+// If v is an invalid semantic version string, Build returns the empty string.
+func Build(v string) string {
+	pv, ok := parse(v)
+	if !ok {
+		return ""
+	}
+	return pv.build
+}
+
+// Compare returns an integer comparing two versions according to
+// semantic version precedence.
+// The result will be 0 if v == w, -1 if v < w, or +1 if v > w.
+//
+// An invalid semantic version string is considered less than a valid one.
+// All invalid semantic version strings compare equal to each other.
+func Compare(v, w string) int {
+	pv, ok1 := parse(v)
+	pw, ok2 := parse(w)
+	if !ok1 && !ok2 {
+		return 0
+	}
+	if !ok1 {
+		return -1
+	}
+	if !ok2 {
+		return +1
+	}
+	if c := compareInt(pv.major, pw.major); c != 0 {
+		return c
+	}
+	if c := compareInt(pv.minor, pw.minor); c != 0 {
+		return c
+	}
+	if c := compareInt(pv.patch, pw.patch); c != 0 {
+		return c
+	}
+	return comparePrerelease(pv.prerelease, pw.prerelease)
+}
+
+// Max canonicalizes its arguments and then returns the version string
+// that compares greater.
+func Max(v, w string) string {
+	v = Canonical(v)
+	w = Canonical(w)
+	if Compare(v, w) > 0 {
+		return v
+	}
+	return w
+}
+
+func parse(v string) (p parsed, ok bool) {
+	if v == "" || v[0] != 'v' {
+		p.err = "missing v prefix"
+		return
+	}
+	p.major, v, ok = parseInt(v[1:])
+	if !ok {
+		p.err = "bad major version"
+		return
+	}
+	if v == "" {
+		p.minor = "0"
+		p.patch = "0"
+		p.short = ".0.0"
+		return
+	}
+	if v[0] != '.' {
+		p.err = "bad minor prefix"
+		ok = false
+		return
+	}
+	p.minor, v, ok = parseInt(v[1:])
+	if !ok {
+		p.err = "bad minor version"
+		return
+	}
+	if v == "" {
+		p.patch = "0"
+		p.short = ".0"
+		return
+	}
+	if v[0] != '.' {
+		p.err = "bad patch prefix"
+		ok = false
+		return
+	}
+	p.patch, v, ok = parseInt(v[1:])
+	if !ok {
+		p.err = "bad patch version"
+		return
+	}
+	if len(v) > 0 && v[0] == '-' {
+		p.prerelease, v, ok = parsePrerelease(v)
+		if !ok {
+			p.err = "bad prerelease"
+			return
+		}
+	}
+	if len(v) > 0 && v[0] == '+' {
+		p.build, v, ok = parseBuild(v)
+		if !ok {
+			p.err = "bad build"
+			return
+		}
+	}
+	if v != "" {
+		p.err = "junk on end"
+		ok = false
+		return
+	}
+	ok = true
+	return
+}
+
+func parseInt(v string) (t, rest string, ok bool) {
+	if v == "" {
+		return
+	}
+	if v[0] < '0' || '9' < v[0] {
+		return
+	}
+	i := 1
+	for i < len(v) && '0' <= v[i] && v[i] <= '9' {
+		i++
+	}
+	if v[0] == '0' && i != 1 {
+		return
+	}
+	return v[:i], v[i:], true
+}
+
+func parsePrerelease(v string) (t, rest string, ok bool) {
+	// "A pre-release version MAY be denoted by appending a hyphen and
+	// a series of dot separated identifiers immediately following the patch version.
+	// Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-].
+	// Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes."
+	if v == "" || v[0] != '-' {
+		return
+	}
+	i := 1
+	start := 1
+	for i < len(v) && v[i] != '+' {
+		if !isIdentChar(v[i]) && v[i] != '.' {
+			return
+		}
+		if v[i] == '.' {
+			if start == i || isBadNum(v[start:i]) {
+				return
+			}
+			start = i + 1
+		}
+		i++
+	}
+	if start == i || isBadNum(v[start:i]) {
+		return
+	}
+	return v[:i], v[i:], true
+}
+
+func parseBuild(v string) (t, rest string, ok bool) {
+	if v == "" || v[0] != '+' {
+		return
+	}
+	i := 1
+	start := 1
+	for i < len(v) {
+		if !isIdentChar(v[i]) && v[i] != '.' {
+			return
+		}
+		if v[i] == '.' {
+			if start == i {
+				return
+			}
+			start = i + 1
+		}
+		i++
+	}
+	if start == i {
+		return
+	}
+	return v[:i], v[i:], true
+}
+
+func isIdentChar(c byte) bool {
+	return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-'
+}
+
+func isBadNum(v string) bool {
+	i := 0
+	for i < len(v) && '0' <= v[i] && v[i] <= '9' {
+		i++
+	}
+	return i == len(v) && i > 1 && v[0] == '0'
+}
+
+func isNum(v string) bool {
+	i := 0
+	for i < len(v) && '0' <= v[i] && v[i] <= '9' {
+		i++
+	}
+	return i == len(v)
+}
+
+func compareInt(x, y string) int {
+	if x == y {
+		return 0
+	}
+	if len(x) < len(y) {
+		return -1
+	}
+	if len(x) > len(y) {
+		return +1
+	}
+	if x < y {
+		return -1
+	}
+	return +1
+}
+
+func comparePrerelease(x, y string) int {
+	// "When major, minor, and patch are equal, a pre-release version has
+	// lower precedence than a normal version.
+	// Example: 1.0.0-alpha < 1.0.0.
+	// Precedence for two pre-release versions with the same major, minor,
+	// and patch version MUST be determined by comparing each dot separated
+	// identifier from left to right until a difference is found as follows:
+	// identifiers consisting of only digits are compared numerically and
+	// identifiers with letters or hyphens are compared lexically in ASCII
+	// sort order. Numeric identifiers always have lower precedence than
+	// non-numeric identifiers. A larger set of pre-release fields has a
+	// higher precedence than a smaller set, if all of the preceding
+	// identifiers are equal.
+	// Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta <
+	// 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0."
+	if x == y {
+		return 0
+	}
+	if x == "" {
+		return +1
+	}
+	if y == "" {
+		return -1
+	}
+	for x != "" && y != "" {
+		x = x[1:] // skip - or .
+		y = y[1:] // skip - or .
+		var dx, dy string
+		dx, x = nextIdent(x)
+		dy, y = nextIdent(y)
+		if dx != dy {
+			ix := isNum(dx)
+			iy := isNum(dy)
+			if ix != iy {
+				if ix {
+					return -1
+				}
+				return +1
+			}
+			if ix {
+				if len(dx) < len(dy) {
+					return -1
+				}
+				if len(dx) > len(dy) {
+					return +1
+				}
+			}
+			if dx < dy {
+				return -1
+			}
+			return +1
+		}
+	}
+	if x == "" {
+		return -1
+	}
+	return +1
+}
+
+func nextIdent(x string) (dx, rest string) {
+	i := 0
+	for i < len(x) && x[i] != '.' {
+		i++
+	}
+	return x[:i], x[i:]
+}


Test patch:
diff --git a/cmd/internal/export/gopkg/exporter_test.go b/cmd/internal/export/gopkg/exporter_test.go
index 68193f15..f3d5bd8d 100644
--- a/cmd/internal/export/gopkg/exporter_test.go
+++ b/cmd/internal/export/gopkg/exporter_test.go
@@ -7,10 +7,11 @@ import (
 	"go/importer"
 	"go/types"
 	"io/ioutil"
-	"log"
 	"os/exec"
 	"strings"
 	"testing"
+
+	"github.com/qiniu/x/log"
 )
 
 // -----------------------------------------------------------------------------
@@ -147,6 +148,27 @@ func TestExportMath(t *testing.T) {
 	}
 }
 
+func TestExportGopQ(t *testing.T) {
+	err := Export("github.com/goplus/gop/ast/gopq", ioutil.Discard)
+	if err != nil {
+		t.Fatal("TestExport failed:", err)
+	}
+}
+
+func TestExportX(t *testing.T) {
+	err := Export("github.com/qiniu/x/log", ioutil.Discard)
+	if err != nil {
+		t.Fatal("TestExport failed:", err)
+	}
+}
+
+func TestExportNotFound(t *testing.T) {
+	err := Export("github.com/qiniu", ioutil.Discard)
+	if err != ErrInvalidPkgPath {
+		t.Fatal("TestExport failed:", err)
+	}
+}
+
 func TestImport(t *testing.T) {
 	pkg, err := Import("go/types")
 	if err != nil {
@@ -204,7 +226,7 @@ func TestExportStd(t *testing.T) {
 		}
 		var buf bytes.Buffer
 		err = ExportPackage(p, &buf)
-		if err != nil && err != ErrorIgnore {
+		if err != nil && err != ErrIgnore {
 			t.Fatal("export error:", pkg, err)
 		}
 		_, err = format.Source(buf.Bytes())