Repository: goplus/gop
Pull request #1325 (2022-07-05 10:35:49)

v1.1.3


Patch:
diff --git a/README.md b/README.md
index cd286ec9..4bab1e3d 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 </p>
 <h1>The Go+ Programming Language</h1>
 
-[goplus.org](https://goplus.org) | [Docs](doc/docs.md) | [Go+ vs. Go](doc/goplus-vs-go.md) | [Playground](https://play.goplus.org) | [Tutorials](https://tutorial.goplus.org/) | [iGo+](https://github.com/goplus/igop) | [Contributing & compiler design](doc/contributing.md)
+[goplus.org](https://goplus.org) | [Docs](doc/docs.md) | [Go+ vs. Go](doc/goplus-vs-go.md) | [Tutorials](https://tutorial.goplus.org/) | [Playground](https://play.goplus.org) | [iGo+ Playground](https://repl.goplus.org/) | [Contributing & compiler design](doc/contributing.md)
 
 </div>
 
@@ -40,7 +40,7 @@ For more details, see [Quick Start](doc/docs.md).
 * Performance: as fast as Go (Go+'s main backend compiles to human-readable Go).
 * Fully compatible with [Go](https://github.com/golang/go) and can mix Go/Go+ code in the same package (see [Go/Go+ hybrid programming](doc/docs.md#gogo-hybrid-programming)).
 * No DSL (Domain Specific Language) support, but it's Specific Domain Friendly (see [DSL vs. SDF](doc/dsl-vs-sdf.md)).
-* Support Go code generation (main backend) and bytecode backend (REPL: see [iGo+](https://github.com/goplus/igop)).
+* Support Go code generation (main backend) and [bytecode backend](https://github.com/goplus/igop) (REPL: see [iGo+](https://repl.goplus.org/)).
 * [Simplest way to interaction with C](doc/docs.md#calling-c-from-go) (cgo is supported but not recommended).
 * [Powerful built-in data processing capabilities](doc/docs.md#data-processing).
 
diff --git a/VERSION b/VERSION
deleted file mode 100644
index 0f1acbd5..00000000
--- a/VERSION
+++ /dev/null
@@ -1,1 +0,0 @@
-v1.1.2
diff --git a/builtin/iox/enum.go b/builtin/iox/enum.go
new file mode 100644
index 00000000..d7be472e
--- /dev/null
+++ b/builtin/iox/enum.go
@@ -0,0 +1,39 @@
+/*
+ Copyright 2022 The GoPlus Authors (goplus.org)
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+     http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package iox
+
+import (
+	"bufio"
+	"io"
+)
+
+// ----------------------------------------------------------------------------
+
+type LineIter struct {
+	*bufio.Scanner
+}
+
+func (it LineIter) Next() (line string, ok bool) {
+	if ok = it.Scan(); ok {
+		line = it.Text()
+	}
+	return
+}
+
+func EnumLines(r io.Reader) LineIter {
+	scanner := bufio.NewScanner(r)
+	return LineIter{scanner}
+}
+
+// ----------------------------------------------------------------------------
diff --git a/cl/builtin.go b/cl/builtin.go
index bb06749c..6c6e5e5c 100644
--- a/cl/builtin.go
+++ b/cl/builtin.go
@@ -32,22 +32,29 @@ func initMathBig(pkg *gox.Package, conf *gox.Config, big *gox.PkgRef) {
 	conf.UntypedBigFloat = big.Ref("UntypedBigfloat").Type().(*types.Named)
 }
 
-func initBuiltin(pkg *gox.Package, builtin *types.Package, fmt, ng, buil *gox.PkgRef) {
+func initBuiltin(pkg *gox.Package, builtin *types.Package, os, fmt, ng, buil *gox.PkgRef) {
 	scope := builtin.Scope()
 	typs := []string{"bigint", "bigrat", "bigfloat"}
 	for _, typ := range typs {
 		name := string(typ[0]-('a'-'A')) + typ[1:]
 		scope.Insert(types.NewTypeName(token.NoPos, builtin, typ, ng.Ref(name).Type()))
 	}
-	fns := []string{
+	fmtFns := []string{
 		"print", "println", "printf", "errorf",
 		"fprint", "fprintln", "fprintf",
 		"sprint", "sprintln", "sprintf",
 	}
-	for _, fn := range fns {
+	for _, fn := range fmtFns {
 		fnTitle := string(fn[0]-'a'+'A') + fn[1:]
 		scope.Insert(gox.NewOverloadFunc(token.NoPos, builtin, fn, fmt.Ref(fnTitle)))
 	}
+	osFns := []string{
+		"open", "create",
+	}
+	for _, fn := range osFns {
+		fnTitle := string(fn[0]-'a'+'A') + fn[1:]
+		scope.Insert(gox.NewOverloadFunc(token.NoPos, builtin, fn, os.Ref(fnTitle)))
+	}
 	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()))
@@ -57,12 +64,13 @@ func initBuiltin(pkg *gox.Package, builtin *types.Package, fmt, ng, buil *gox.Pk
 func newBuiltinDefault(pkg *gox.Package, conf *gox.Config) *types.Package {
 	builtin := types.NewPackage("", "")
 	fmt := pkg.Import("fmt")
+	os := pkg.Import("os")
 	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, ng)
-	initBuiltin(pkg, builtin, fmt, ng, buil)
+	initBuiltin(pkg, builtin, os, fmt, ng, buil)
 	gox.InitBuiltin(pkg, builtin, conf)
 	return builtin
 }
diff --git a/cl/compile.go b/cl/compile.go
index 591d0537..d0d070e5 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -345,6 +345,10 @@ const (
 	testingGoFile  = "_test"
 )
 
+const (
+	ioxPkgPath = "github.com/goplus/gop/builtin/iox"
+)
+
 // NewPackage creates a Go+ package instance.
 func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package, err error) {
 	workingDir := conf.WorkingDir
@@ -372,6 +376,7 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package,
 		NewBuiltin:      newBuiltinDefault,
 		DefaultGoFile:   defaultGoFile,
 		NoSkipConstant:  conf.NoSkipConstant,
+		PkgPathIox:      ioxPkgPath,
 	}
 	if enableRecover {
 		defer func() {
diff --git a/cmd/internal/base/pass.go b/cmd/internal/base/pass.go
new file mode 100644
index 00000000..729c1afc
--- /dev/null
+++ b/cmd/internal/base/pass.go
@@ -0,0 +1,73 @@
+package base
+
+import (
+	"flag"
+	"fmt"
+)
+
+type stringValue struct {
+	p    *PassArgs
+	name string
+}
+
+func (p *stringValue) String() string {
+	return ""
+}
+
+func (p *stringValue) Set(v string) error {
+	p.p.Args = append(p.p.Args, fmt.Sprintf("-%v=%v", p.name, v))
+	return nil
+}
+
+type boolValue struct {
+	p    *PassArgs
+	name string
+}
+
+func (p *boolValue) String() string {
+	return ""
+}
+
+func (p *boolValue) Set(v string) error {
+	p.p.Args = append(p.p.Args, fmt.Sprintf("-%v=%v", p.name, v))
+	return nil
+}
+
+func (p *boolValue) IsBoolFlag() bool {
+	return true
+}
+
+type PassArgs struct {
+	Args []string
+	Flag *flag.FlagSet
+}
+
+func (p *PassArgs) Var(names ...string) {
+	for _, name := range names {
+		p.Flag.Var(&stringValue{p: p, name: name}, name, "")
+	}
+}
+
+func (p *PassArgs) Bool(names ...string) {
+	for _, name := range names {
+		p.Flag.Var(&boolValue{p: p, name: name}, name, "")
+	}
+}
+
+func NewPassArgs(flag *flag.FlagSet) *PassArgs {
+	p := &PassArgs{Flag: flag}
+	return p
+}
+
+func PassBuildFlags(cmd *Command) *PassArgs {
+	p := NewPassArgs(&cmd.Flag)
+	p.Bool("v")
+	p.Bool("n", "x")
+	p.Bool("a")
+	p.Bool("linkshared", "race", "msan", "asan",
+		"trimpath", "work")
+	p.Var("p", "asmflags", "compiler", "buildmode",
+		"gcflags", "gccgoflags", "installsuffix",
+		"ldflags", "pkgdir", "tags", "toolexec", "buildvcs")
+	return p
+}
diff --git a/cmd/internal/build/build.go b/cmd/internal/build/build.go
index 068dc28c..365bf0e2 100644
--- a/cmd/internal/build/build.go
+++ b/cmd/internal/build/build.go
@@ -36,27 +36,28 @@ import (
 
 // gop build
 var Cmd = &base.Command{
-	UsageLine: "gop build [-v -o output] [packages]",
+	UsageLine: "gop build [-debug -o output] [packages]",
 	Short:     "Build Go+ files",
 }
 
 var (
-	flagVerbose = flag.Bool("v", false, "print verbose information")
-	flagOutput  = flag.String("o", "", "gop build output file")
-	flag        = &Cmd.Flag
+	flagDebug  = flag.Bool("debug", false, "print debug information")
+	flagOutput = flag.String("o", "", "gop build output file")
+	flag       = &Cmd.Flag
 )
 
 func init() {
 	Cmd.Run = runCmd
 }
 
-func runCmd(_ *base.Command, args []string) {
+func runCmd(cmd *base.Command, args []string) {
+	pass := base.PassBuildFlags(cmd)
 	err := flag.Parse(args)
 	if err != nil {
 		log.Panicln("parse input arguments failed:", err)
 	}
 
-	if *flagVerbose {
+	if *flagDebug {
 		gox.SetDebug(gox.DbgFlagAll &^ gox.DbgFlagComments)
 		cl.SetDebug(cl.DbgFlagAll)
 		cl.SetDisableRecover(true)
@@ -85,6 +86,7 @@ func runCmd(_ *base.Command, args []string) {
 		}
 		confCmd.Flags = []string{"-o", output}
 	}
+	confCmd.Flags = append(confCmd.Flags, pass.Args...)
 	build(proj, conf, confCmd)
 }
 
diff --git a/cmd/internal/install/install.go b/cmd/internal/install/install.go
index bf37a94d..cb321756 100644
--- a/cmd/internal/install/install.go
+++ b/cmd/internal/install/install.go
@@ -35,20 +35,21 @@ import (
 
 // gop install
 var Cmd = &base.Command{
-	UsageLine: "gop install [-v] [packages]",
+	UsageLine: "gop install [-debug] [packages]",
 	Short:     "Build Go+ files and install target to GOBIN",
 }
 
 var (
-	flag        = &Cmd.Flag
-	flagVerbose = flag.Bool("v", false, "print verbose information")
+	flag      = &Cmd.Flag
+	flagDebug = flag.Bool("debug", false, "print debug information")
 )
 
 func init() {
 	Cmd.Run = runCmd
 }
 
-func runCmd(_ *base.Command, args []string) {
+func runCmd(cmd *base.Command, args []string) {
+	pass := base.PassBuildFlags(cmd)
 	err := flag.Parse(args)
 	if err != nil {
 		log.Fatalln("parse input arguments failed:", err)
@@ -64,7 +65,7 @@ func runCmd(_ *base.Command, args []string) {
 		log.Panicln("gopprojs.ParseAll:", err)
 	}
 
-	if *flagVerbose {
+	if *flagDebug {
 		gox.SetDebug(gox.DbgFlagAll &^ gox.DbgFlagComments)
 		cl.SetDebug(cl.DbgFlagAll)
 		cl.SetDisableRecover(true)
@@ -73,6 +74,7 @@ func runCmd(_ *base.Command, args []string) {
 	gopEnv := gopenv.Get()
 	conf := &gop.Config{Gop: gopEnv}
 	confCmd := &gocmd.Config{Gop: gopEnv}
+	confCmd.Flags = pass.Args
 	for _, proj := range projs {
 		install(proj, conf, confCmd)
 	}
diff --git a/cmd/internal/run/run.go b/cmd/internal/run/run.go
index ff6c8b4a..fcf1fa03 100644
--- a/cmd/internal/run/run.go
+++ b/cmd/internal/run/run.go
@@ -42,9 +42,8 @@ var Cmd = &base.Command{
 var (
 	flag        = &Cmd.Flag
 	flagAsm     = flag.Bool("asm", false, "generates `asm` code of Go+ bytecode backend")
-	flagVerbose = flag.Bool("v", false, "print verbose information")
+	flagDebug   = flag.Bool("debug", false, "print debug information")
 	flagQuiet   = flag.Bool("quiet", false, "don't generate any compiling stage log")
-	flagDebug   = flag.Bool("debug", false, "set log level to debug")
 	flagNoChdir = flag.Bool("nc", false, "don't change dir (only for `gop run pkgPath`)")
 	flagProf    = flag.Bool("prof", false, "do profile and generate profile report")
 )
@@ -54,6 +53,7 @@ func init() {
 }
 
 func runCmd(cmd *base.Command, args []string) {
+	pass := base.PassBuildFlags(cmd)
 	err := flag.Parse(args)
 	if err != nil {
 		log.Fatalln("parse input arguments failed:", err)
@@ -69,14 +69,10 @@ func runCmd(cmd *base.Command, args []string) {
 
 	if *flagQuiet {
 		log.SetOutputLevel(0x7000)
-	} else if *flagVerbose {
+	} else if *flagDebug {
 		gox.SetDebug(gox.DbgFlagAll &^ gox.DbgFlagComments)
 		cl.SetDebug(cl.DbgFlagAll)
 		cl.SetDisableRecover(true)
-	} else if *flagDebug {
-		log.SetOutputLevel(log.Ldebug)
-		gox.SetDebug(gox.DbgFlagAll)
-		cl.SetDebug(cl.DbgFlagAll)
 	} else if *flagAsm {
 		gox.SetDebug(gox.DbgFlagInstruction)
 	}
@@ -89,6 +85,7 @@ func runCmd(cmd *base.Command, args []string) {
 	gopEnv := gopenv.Get()
 	conf := &gop.Config{Gop: gopEnv}
 	confCmd := &gocmd.Config{Gop: gopEnv}
+	confCmd.Flags = pass.Args
 	run(proj, args, !noChdir, conf, confCmd)
 }
 
diff --git a/doc/docs.md b/doc/docs.md
index d45741bc..da861434 100644
--- a/doc/docs.md
+++ b/doc/docs.md
@@ -1081,7 +1081,7 @@ b := a - 1/3r + 3 * 1/2r
 println a, b // 4/5 59/30
 ```
 
-Casting rational numbers works like other [primitive types](#primitive-types)):
+Casting rational numbers works like other [primitive types](#primitive-types):
 
 ```go
 a := 1r
diff --git a/go.mod b/go.mod
index 2fc4a25a..680d826c 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.16
 
 require (
 	github.com/goplus/c2go v0.7.8
-	github.com/goplus/gox v1.11.12
+	github.com/goplus/gox v1.11.15
 	github.com/goplus/libc v0.3.10
 	github.com/goplus/mod v0.9.12
 	github.com/qiniu/x v1.11.9
diff --git a/go.sum b/go.sum
index 2cf1ca6b..ed894547 100644
--- a/go.sum
+++ b/go.sum
@@ -4,8 +4,9 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/goplus/c2go v0.7.8 h1:XI7A9/5l4kXShaEhJj4ei8VprCiaCpX6lnwudosOtuU=
 github.com/goplus/c2go v0.7.8/go.mod h1:6D9HpX+q/8/dmTT08l7sCpWyDTh22Sx3rJk83OvyVu8=
-github.com/goplus/gox v1.11.12 h1:zb+hNzKbUJ16tDyGlwDIHzdArRLeDz+wXaBsNXeSDew=
 github.com/goplus/gox v1.11.12/go.mod h1:wRCRSNukie4cDqADF4w0Btc2Gk6V3p3V6hI5+rsVqa8=
+github.com/goplus/gox v1.11.15 h1:5XUruNjUo7QQyIqECaLomv9eEaKEqC3OY8dSCeWZbR0=
+github.com/goplus/gox v1.11.15/go.mod h1:wRCRSNukie4cDqADF4w0Btc2Gk6V3p3V6hI5+rsVqa8=
 github.com/goplus/libc v0.3.10 h1:5kEwgx3HP0zMPGZsvgKwAiLAmMS6aDgoBhniBSWF/ok=
 github.com/goplus/libc v0.3.10/go.mod h1:gSQ8dVF/iKdoBn1ABeQfAF/ybaYX0pij3iPtC72FtJc=
 github.com/goplus/mod v0.9.12 h1:CjgBGQIYqUTPGl3MrAS5CICzJwxbIfSa4OlEb141Gs4=
diff --git a/parser/_nofmt/cmdlinestyle1/cmd.gop b/parser/_nofmt/cmdlinestyle1/cmd.gop
new file mode 100644
index 00000000..3da8f4c4
--- /dev/null
+++ b/parser/_nofmt/cmdlinestyle1/cmd.gop
@@ -0,0 +1,10 @@
+package main
+
+func main() {
+	changeYpos -0.7, 8
+	changeYpos -0.7
+	changeYpos - 0.7
+	changeYpos-0.7
+	x[1]
+	x []...
+}
diff --git a/parser/_nofmt/cmdlinestyle1/parser.expect b/parser/_nofmt/cmdlinestyle1/parser.expect
new file mode 100644
index 00000000..2573a7b0
--- /dev/null
+++ b/parser/_nofmt/cmdlinestyle1/parser.expect
@@ -0,0 +1,83 @@
+package main
+
+file cmd.gop
+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: changeYpos
+              Args:
+                ast.UnaryExpr:
+                  Op: -
+                  X:
+                    ast.BasicLit:
+                      Kind: FLOAT
+                      Value: 0.7
+                ast.BasicLit:
+                  Kind: INT
+                  Value: 8
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: changeYpos
+              Args:
+                ast.UnaryExpr:
+                  Op: -
+                  X:
+                    ast.BasicLit:
+                      Kind: FLOAT
+                      Value: 0.7
+        ast.ExprStmt:
+          X:
+            ast.BinaryExpr:
+              X:
+                ast.Ident:
+                  Name: changeYpos
+              Op: -
+              Y:
+                ast.BasicLit:
+                  Kind: FLOAT
+                  Value: 0.7
+        ast.ExprStmt:
+          X:
+            ast.BinaryExpr:
+              X:
+                ast.Ident:
+                  Name: changeYpos
+              Op: -
+              Y:
+                ast.BasicLit:
+                  Kind: FLOAT
+                  Value: 0.7
+        ast.ExprStmt:
+          X:
+            ast.IndexExpr:
+              X:
+                ast.Ident:
+                  Name: x
+              Index:
+                ast.BasicLit:
+                  Kind: INT
+                  Value: 1
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: x
+              Args:
+                ast.SliceLit:
diff --git a/parser/_nofmt/cmdlinestyle2/cmd2.gop b/parser/_nofmt/cmdlinestyle2/cmd2.gop
new file mode 100644
index 00000000..f09fdeca
--- /dev/null
+++ b/parser/_nofmt/cmdlinestyle2/cmd2.gop
@@ -0,0 +1,1 @@
+add(100, 200) (0).Test()
diff --git a/parser/_nofmt/cmdlinestyle2/parser.expect b/parser/_nofmt/cmdlinestyle2/parser.expect
new file mode 100644
index 00000000..5a5325a1
--- /dev/null
+++ b/parser/_nofmt/cmdlinestyle2/parser.expect
@@ -0,0 +1,41 @@
+package main
+
+file cmd2.gop
+noEntrypoint
+ast.FuncDecl:
+  Name:
+    ast.Ident:
+      Name: main
+  Type:
+    ast.FuncType:
+      Params:
+        ast.FieldList:
+  Body:
+    ast.BlockStmt:
+      List:
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.SelectorExpr:
+                  X:
+                    ast.CallExpr:
+                      Fun:
+                        ast.CallExpr:
+                          Fun:
+                            ast.Ident:
+                              Name: add
+                          Args:
+                            ast.BasicLit:
+                              Kind: INT
+                              Value: 100
+                            ast.BasicLit:
+                              Kind: INT
+                              Value: 200
+                      Args:
+                        ast.BasicLit:
+                          Kind: INT
+                          Value: 0
+                  Sel:
+                    ast.Ident:
+                      Name: Test
diff --git a/parser/_nofmt/cmdlinestyle3/cmd.gop b/parser/_nofmt/cmdlinestyle3/cmd.gop
new file mode 100644
index 00000000..912bcba5
--- /dev/null
+++ b/parser/_nofmt/cmdlinestyle3/cmd.gop
@@ -0,0 +1,3 @@
+println (1+2i, 2)
+println (1, a...)
+println (a...)
diff --git a/parser/_nofmt/cmdlinestyle3/parser.expect b/parser/_nofmt/cmdlinestyle3/parser.expect
new file mode 100644
index 00000000..8e9f7eaa
--- /dev/null
+++ b/parser/_nofmt/cmdlinestyle3/parser.expect
@@ -0,0 +1,56 @@
+package main
+
+file cmd.gop
+noEntrypoint
+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: println
+              Args:
+                ast.BinaryExpr:
+                  X:
+                    ast.BasicLit:
+                      Kind: INT
+                      Value: 1
+                  Op: +
+                  Y:
+                    ast.BasicLit:
+                      Kind: IMAG
+                      Value: 2i
+                ast.BasicLit:
+                  Kind: INT
+                  Value: 2
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: println
+              Args:
+                ast.BasicLit:
+                  Kind: INT
+                  Value: 1
+                ast.Ident:
+                  Name: a
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: println
+              Args:
+                ast.Ident:
+                  Name: a
diff --git a/parser/parser.go b/parser/parser.go
index 264a601d..711904fc 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -40,6 +40,10 @@ import (
 	"github.com/qiniu/x/log"
 )
 
+const (
+	msgTupleNotSupported = "tuple is not supported"
+)
+
 // The parser structure holds the parser's internal state.
 type parser struct {
 	file    *token.File
@@ -484,7 +488,6 @@ func (p *parser) atComma(context string, follow token.Token) bool {
 		p.error(p.pos, msgctx)
 		if debugParseError {
 			log.Std.Output("", log.Linfo, 2, msgctx)
-			panic(msgctx)
 		}
 		return true // "insert" comma and continue
 	}
@@ -1352,89 +1355,70 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr {
 	return &ast.FuncLit{Type: typ, Body: body}
 }
 
-func (p *parser) isCommand(x ast.Expr) bool {
-	switch x.(type) {
-	case *ast.Ident, *ast.SelectorExpr, *ast.ErrWrapExpr:
-	default:
-		return false
-	}
-	switch p.tok {
-	case token.IDENT, token.RARROW,
-		token.STRING, token.CSTRING, token.INT, token.FLOAT, token.IMAG, token.CHAR, token.RAT,
-		token.FUNC, token.GOTO, token.MAP, token.INTERFACE, token.CHAN, token.STRUCT:
-		return true
-	case token.SUB, token.NOT, token.AND, token.MUL, token.ARROW, token.XOR, token.ADD:
-		if x.End() == p.pos { // x-y
-			return false
-		}
-		oldtok, oldpos := p.tok, p.pos
-		p.next()
-		newpos := int(p.pos)
-		p.unget(oldpos, oldtok, "")
-		return int(oldpos)+len(oldtok.String()) == newpos // x -y
-	}
-	return false
-}
-
 // parseOperand may return an expression or a raw type (incl. array
 // types of the form [...]T. Callers must verify the result.
 // If lhs is set and the result is an identifier, it is not resolved.
 //
-func (p *parser) parseOperand(lhs, allowTuple, allowCmd bool) ast.Expr {
+func (p *parser) parseOperand(lhs, allowTuple, allowCmd bool) (x ast.Expr, isTuple bool) {
 	if p.trace {
 		defer un(trace(p, "Operand"))
 	}
 
 	switch p.tok {
 	case token.IDENT:
-		x := p.parseIdent()
+		x = p.parseIdent()
 		if !lhs {
 			p.resolve(x)
 		}
-		return x
+		return
 
 	case token.STRING, token.CSTRING, token.INT, token.FLOAT, token.IMAG, token.CHAR, token.RAT:
-		x := &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
+		x = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
 		if debugParseOutput {
 			log.Printf("ast.BasicLit{Kind: %v, Value: %v}\n", p.tok, p.lit)
 		}
 		p.next()
-		return x
+		return
 
 	case token.LPAREN:
 		lparen := p.pos
 		p.next()
 		if allowTuple && p.tok == token.RPAREN { // () => expr
 			p.next()
-			return &tupleExpr{}
+			return &tupleExpr{opening: lparen, closing: p.pos}, true
 		}
 		p.exprLev++
-		x := p.parseRHSOrType() // types may be parenthesized: (some type)
-		if allowTuple && p.tok == token.COMMA {
+		x = p.parseRHSOrType() // types may be parenthesized: (some type)
+		if allowTuple && (p.tok == token.COMMA || p.tok == token.ELLIPSIS) {
 			// (x, y, ...) => expr
-			items := make([]*ast.Ident, 1, 2)
-			items[0] = p.toIdent(x)
+			items := make([]ast.Expr, 1, 2)
+			items[0] = x
 			for p.tok == token.COMMA {
 				p.next()
-				items = append(items, p.parseIdent())
+				items = append(items, p.parseRHSOrType())
+			}
+			t := &tupleExpr{opening: lparen, items: items, closing: p.pos}
+			if p.tok == token.ELLIPSIS {
+				t.ellipsis = p.pos
+				p.next()
 			}
 			p.exprLev--
 			p.expect(token.RPAREN)
-			return &tupleExpr{items: items}
+			return t, true
 		}
 		p.exprLev--
 		rparen := p.expect(token.RPAREN)
 		if debugParseOutput {
 			log.Printf("ast.ParenExpr{X: %v}\n", x)
 		}
-		return &ast.ParenExpr{Lparen: lparen, X: x, Rparen: rparen}
+		return &ast.ParenExpr{Lparen: lparen, X: x, Rparen: rparen}, false
 
 	case token.FUNC:
-		return p.parseFuncTypeOrLit()
+		return p.parseFuncTypeOrLit(), false
 
 	case token.LBRACE:
 		if !lhs { // rhs: mapLit - {k1: v1, k2: v2, ...}
-			return p.parseLiteralValueOrMapComprehension()
+			return p.parseLiteralValueOrMapComprehension(), false
 		}
 
 	case token.MAP:
@@ -1450,29 +1434,29 @@ func (p *parser) parseOperand(lhs, allowTuple, allowCmd bool) ast.Expr {
 		// token.RANGE, token.IMPORT, token.TYPE, token.SELECT, token.INTERFACE:
 		// Go+: allow goto() as a function
 		p.tok = token.IDENT
-		x := p.parseIdent()
+		x = p.parseIdent()
 		if !lhs {
 			p.resolve(x)
 		}
-		return x
+		return
 	}
 
 	typ, result := p.tryIdentOrType(stateArrayTypeOrSliceLit, nil)
 	if (result & resultExprFlags) != 0 { // is an expr, not a type
-		return typ
+		return typ, false
 	}
 	if typ != nil {
 		// could be type for composite literal or conversion
 		_, isIdent := typ.(*ast.Ident)
 		assert(!isIdent, "type cannot be identifier")
-		return typ
+		return typ, false
 	}
 
 	// we have an error
 	pos := p.pos
 	p.errorExpected(pos, "operand", 2)
 	p.advance(stmtStart)
-	return &ast.BadExpr{From: pos, To: p.pos}
+	return &ast.BadExpr{From: pos, To: p.pos}, false
 }
 
 func (p *parser) parseSelector(x ast.Expr) ast.Expr {
@@ -1568,7 +1552,6 @@ func (p *parser) parseCallOrConversion(fun ast.Expr, isCmd bool) *ast.CallExpr {
 	if p.trace {
 		defer un(trace(p, "CallOrConversion"))
 	}
-
 	var lparen, rparen token.Pos
 	var endTok token.Token
 	if isCmd {
@@ -1580,7 +1563,18 @@ func (p *parser) parseCallOrConversion(fun ast.Expr, isCmd bool) *ast.CallExpr {
 	var list []ast.Expr
 	var ellipsis token.Pos
 	for p.tok != endTok && p.tok != token.EOF && !ellipsis.IsValid() {
-		list = append(list, p.parseRHSOrType()) // builtins may expect a type: make(some type, ...)
+		expr, isTuple := p.parseRHSOrTypeEx(isCmd && len(list) == 0)
+		if isTuple {
+			t := expr.(*tupleExpr)
+			if p.tok != token.SEMICOLON && p.tok != token.RBRACE && p.tok != token.EOF {
+				p.error(t.opening, msgTupleNotSupported)
+				p.advance(stmtStart)
+			}
+			list, lparen, ellipsis, rparen = t.items, t.opening, t.ellipsis, t.closing
+			isCmd = true
+			break
+		}
+		list = append(list, expr) // builtins may expect a type: make(some type, ...)
 		if p.tok == token.ELLIPSIS {
 			ellipsis = p.pos
 			p.next()
@@ -1600,7 +1594,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr, isCmd bool) *ast.CallExpr {
 	var noParenEnd token.Pos
 	if isCmd {
 		noParenEnd = p.pos
-	} else {
+	} else if rparen == token.NoPos {
 		rparen = p.expectClosing(token.RPAREN, "argument list")
 	}
 	if debugParseOutput {
@@ -1752,7 +1746,7 @@ func (p *parser) parseLiteralValue(typ ast.Expr) ast.Expr {
 
 // checkExpr checks that x is an expression (and not a type).
 func (p *parser) checkExpr(x ast.Expr) ast.Expr {
-	switch unparen(x).(type) {
+	switch v := unparen(x).(type) {
 	case *ast.BadExpr:
 	case *ast.Ident:
 	case *ast.BasicLit:
@@ -1760,8 +1754,6 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr {
 	case *ast.CompositeLit:
 	case *ast.SliceLit:
 	case *ast.ComprehensionExpr:
-	case *ast.ParenExpr:
-		panic("unreachable")
 	case *ast.SelectorExpr:
 	case *ast.IndexExpr:
 	case *ast.SliceExpr:
@@ -1779,6 +1771,9 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr {
 	case *ast.ErrWrapExpr:
 	case *ast.LambdaExpr:
 	case *ast.LambdaExpr2:
+	case *tupleExpr:
+		p.error(v.opening, msgTupleNotSupported)
+		x = &ast.BadExpr{From: v.opening, To: v.closing}
 	default:
 		// all other nodes are not proper expressions
 		p.errorExpected(x.Pos(), "expression", 3)
@@ -1836,29 +1831,27 @@ func unparen(x ast.Expr) ast.Expr {
 
 // checkExprOrType checks that x is an expression or a type
 // (and not a raw type such as [...]T).
-//
 func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
 	switch t := unparen(x).(type) {
-	case *ast.ParenExpr:
-		panic("unreachable")
 	case *ast.ArrayType:
 		if len, isEllipsis := t.Len.(*ast.Ellipsis); isEllipsis {
 			p.error(len.Pos(), "expected array length, found '...'")
 			x = &ast.BadExpr{From: x.Pos(), To: p.safePos(x.End())}
 		}
 	}
-
 	// all other nodes are expressions or types
 	return x
 }
 
 // If lhs is set and the result is an identifier, it is not resolved.
-func (p *parser) parsePrimaryExpr(lhs, allowTuple, allowCmd bool) ast.Expr {
+func (p *parser) parsePrimaryExpr(lhs, allowTuple, allowCmd bool) (x ast.Expr, isTuple bool) {
 	if p.trace {
 		defer un(trace(p, "PrimaryExpr"))
 	}
 
-	x := p.parseOperand(lhs, allowTuple, allowCmd)
+	if x, isTuple = p.parseOperand(lhs, allowTuple, allowCmd); isTuple {
+		return
+	}
 L:
 	for {
 		switch p.tok {
@@ -1887,18 +1880,19 @@ L:
 			if lhs {
 				p.resolve(x)
 			}
-			if allowCmd && x.End() != p.pos { // println []
+			if allowCmd && p.isCmd(x) { // println [...]
 				x = p.parseCallOrConversion(p.checkExprOrType(x), true)
 			} else {
 				x = p.parseIndexOrSlice(p.checkExpr(x))
 			}
-		case token.LPAREN:
+		case token.LPAREN: // (
 			if lhs {
 				p.resolve(x)
 			}
-			x = p.parseCallOrConversion(p.checkExprOrType(x), false)
+			isCmd := allowCmd && p.isCmd(x) // println (...)
+			x = p.parseCallOrConversion(p.checkExprOrType(x), isCmd)
 		case token.LBRACE: // {
-			if allowCmd && x.End() != p.pos { // println {}
+			if allowCmd && p.isCmd(x) { // println {...}
 				x = p.parseCallOrConversion(p.checkExprOrType(x), true)
 			} else if isLiteralType(x) && (p.exprLev >= 0 || !isTypeName(x)) {
 				if lhs {
@@ -1912,7 +1906,7 @@ L:
 			x = &ast.ErrWrapExpr{X: x, Tok: p.tok, TokPos: p.pos}
 			p.next()
 		default:
-			if allowCmd && p.isCommand(x) {
+			if allowCmd && p.isCmd(x) && p.checkCmd(x) {
 				if lhs {
 					p.resolve(x)
 				}
@@ -1923,23 +1917,49 @@ L:
 		}
 		lhs = false // no need to try to resolve again
 	}
-	return x
+	return
+}
+
+func (p *parser) isCmd(x ast.Expr) bool {
+	switch x.(type) {
+	case *ast.Ident, *ast.SelectorExpr, *ast.ErrWrapExpr:
+		return x.End() != p.pos
+	}
+	return false
+}
+
+func (p *parser) checkCmd(x ast.Expr) bool {
+	switch p.tok {
+	case token.IDENT, token.RARROW,
+		token.STRING, token.CSTRING, token.INT, token.FLOAT, token.IMAG, token.CHAR, token.RAT,
+		token.FUNC, token.GOTO, token.MAP, token.INTERFACE, token.CHAN, token.STRUCT:
+		return true
+	case token.SUB, token.AND, token.MUL, token.ARROW, token.XOR, token.ADD:
+		oldtok, oldpos := p.tok, p.pos
+		p.next()
+		newpos := int(p.pos)
+		p.unget(oldpos, oldtok, "")
+		return int(oldpos)+len(oldtok.String()) == newpos // x -y
+	}
+	return false
 }
 
 // parseErrWrapExpr: expr! expr? expr?:defval
-func (p *parser) parseErrWrapExpr(lhs, allowTuple, allowCmd bool) ast.Expr {
-	x := p.parsePrimaryExpr(lhs, allowTuple, allowCmd)
+func (p *parser) parseErrWrapExpr(lhs, allowTuple, allowCmd bool) (x ast.Expr, isTuple bool) {
+	if x, isTuple = p.parsePrimaryExpr(lhs, allowTuple, allowCmd); isTuple {
+		return
+	}
 	if expr, ok := x.(*ast.ErrWrapExpr); ok {
 		if p.tok == token.COLON {
 			p.next()
-			expr.Default = p.parseUnaryExpr(false, true, false)
+			expr.Default, _ = p.parseUnaryExpr(false, false, false)
 		}
 	}
-	return x
+	return
 }
 
 // If lhs is set and the result is an identifier, it is not resolved.
-func (p *parser) parseUnaryExpr(lhs, allowTuple, allowCmd bool) ast.Expr {
+func (p *parser) parseUnaryExpr(lhs, allowTuple, allowCmd bool) (ast.Expr, bool) {
 	if p.trace {
 		defer un(trace(p, "UnaryExpr"))
 	}
@@ -1948,8 +1968,8 @@ func (p *parser) parseUnaryExpr(lhs, allowTuple, allowCmd bool) ast.Expr {
 	case token.ADD, token.SUB, token.NOT, token.XOR, token.AND:
 		pos, op := p.pos, p.tok
 		p.next()
-		x := p.parseUnaryExpr(false, false, false)
-		return &ast.UnaryExpr{OpPos: pos, Op: op, X: p.checkExpr(x)}
+		x, _ := p.parseUnaryExpr(false, false, false)
+		return &ast.UnaryExpr{OpPos: pos, Op: op, X: p.checkExpr(x)}, false
 
 	case token.ARROW:
 		// channel type or receive expression
@@ -1970,7 +1990,7 @@ func (p *parser) parseUnaryExpr(lhs, allowTuple, allowCmd bool) ast.Expr {
 		//   <- (chan type)    =>  (<-chan type)
 		//   <- (chan<- type)  =>  (<-chan (<-type))
 
-		x := p.parseUnaryExpr(false, false, false)
+		x, _ := p.parseUnaryExpr(false, false, false)
 
 		// determine which case we have
 		if typ, ok := x.(*ast.ChanType); ok {
@@ -1991,18 +2011,18 @@ func (p *parser) parseUnaryExpr(lhs, allowTuple, allowCmd bool) ast.Expr {
 				p.errorExpected(arrow, "channel type", 2)
 			}
 
-			return x
+			return x, false
 		}
 
 		// <-(expr)
-		return &ast.UnaryExpr{OpPos: arrow, Op: token.ARROW, X: p.checkExpr(x)}
+		return &ast.UnaryExpr{OpPos: arrow, Op: token.ARROW, X: p.checkExpr(x)}, false
 
 	case token.MUL:
 		// pointer type or unary "*" expression
 		pos := p.pos
 		p.next()
-		x := p.parseUnaryExpr(false, false, false)
-		return &ast.StarExpr{Star: pos, X: p.checkExprOrType(x)}
+		x, _ := p.parseUnaryExpr(false, false, false)
+		return &ast.StarExpr{Star: pos, X: p.checkExprOrType(x)}, false
 	}
 
 	return p.parseErrWrapExpr(lhs, allowTuple, allowCmd)
@@ -2017,64 +2037,67 @@ func (p *parser) tokPrec() (token.Token, int) {
 }
 
 // If lhs is set and the result is an identifier, it is not resolved.
-func (p *parser) parseBinaryExpr(lhs bool, prec1 int, allowTuple, allowCmd bool) ast.Expr {
+func (p *parser) parseBinaryExpr(lhs bool, prec1 int, allowTuple, allowCmd bool) (x ast.Expr, isTuple bool) {
 	if p.trace {
 		defer un(trace(p, "BinaryExpr"))
 	}
 
-	x := p.parseUnaryExpr(lhs, allowTuple, allowCmd)
+	if x, isTuple = p.parseUnaryExpr(lhs, allowTuple, allowCmd); isTuple {
+		return
+	}
 	for {
 		op, oprec := p.tokPrec()
 		if oprec < prec1 {
-			return x
+			return
 		}
 		pos := p.expect(op)
 		if lhs {
 			p.resolve(x)
 			lhs = false
 		}
-		y := p.parseBinaryExpr(false, oprec+1, false, false)
+		y, _ := p.parseBinaryExpr(false, oprec+1, false, false)
 		x = &ast.BinaryExpr{X: p.checkExpr(x), OpPos: pos, Op: op, Y: p.checkExpr(y)}
 	}
 }
 
-func (p *parser) parseRangeExpr(allowCmd bool) ast.Expr {
-	var low ast.Expr
+func (p *parser) parseRangeExpr(allowTuple, allowCmd bool) (x ast.Expr, isTuple bool) {
 	if p.tok != token.COLON {
-		low = p.parseBinaryExpr(false, token.LowestPrec+1, true, allowCmd)
-		if p.tok != token.COLON { // not RangeExpr
-			return low
+		x, isTuple = p.parseBinaryExpr(false, token.LowestPrec+1, allowTuple, allowCmd)
+		if isTuple || p.tok != token.COLON { // not RangeExpr
+			return
 		}
 	}
 	to := p.pos
 	p.next()
-	high := p.parseBinaryExpr(false, token.LowestPrec+1, false, false)
+	high, _ := p.parseBinaryExpr(false, token.LowestPrec+1, false, false)
 	var colon2 token.Pos
 	var expr3 ast.Expr
 	if p.tok == token.COLON {
 		colon2 = p.pos
 		p.next()
-		expr3 = p.parseBinaryExpr(false, token.LowestPrec+1, false, false)
+		expr3, _ = p.parseBinaryExpr(false, token.LowestPrec+1, false, false)
 	}
 	if debugParseOutput {
-		log.Printf("ast.RangeExpr{First: %v, Last: %v, Expr3: %v}\n", low, high, expr3)
+		log.Printf("ast.RangeExpr{First: %v, Last: %v, Expr3: %v}\n", x, high, expr3)
 	}
-	return &ast.RangeExpr{First: low, To: to, Last: high, Colon2: colon2, Expr3: expr3}
+	return &ast.RangeExpr{First: x, To: to, Last: high, Colon2: colon2, Expr3: expr3}, false
 }
 
 type tupleExpr struct {
 	ast.Expr
-	items []*ast.Ident
+	opening  token.Pos
+	items    []ast.Expr
+	ellipsis token.Pos
+	closing  token.Pos
 }
 
-func (p *parser) parseLambdaExpr(allowCmd, allowRangeExpr bool) ast.Expr {
-	var x ast.Expr
+func (p *parser) parseLambdaExpr(allowTuple, allowCmd, allowRangeExpr bool) (x ast.Expr, isTuple bool) {
 	var first = p.pos
 	if p.tok != token.RARROW {
 		if allowRangeExpr {
-			x = p.parseRangeExpr(allowCmd)
+			x, isTuple = p.parseRangeExpr(true, allowCmd)
 		} else {
-			x = p.parseBinaryExpr(false, token.LowestPrec+1, true, allowCmd)
+			x, isTuple = p.parseBinaryExpr(false, token.LowestPrec+1, true, allowCmd)
 		}
 	}
 	if p.tok == token.RARROW { // =>
@@ -2107,7 +2130,11 @@ func (p *parser) parseLambdaExpr(allowCmd, allowRangeExpr bool) ast.Expr {
 		retry:
 			switch v := e.(type) {
 			case *tupleExpr:
-				lhs, lhsHasParen = v.items, true
+				items := make([]*ast.Ident, len(v.items))
+				for i, item := range v.items {
+					items[i] = p.toIdent(item)
+				}
+				lhs, lhsHasParen = items, true
 			case *ast.ParenExpr:
 				e, lhsHasParen = v.X, true
 				goto retry
@@ -2125,7 +2152,7 @@ func (p *parser) parseLambdaExpr(allowCmd, allowRangeExpr bool) ast.Expr {
 				Rarrow:      rarrow,
 				Body:        body,
 				LhsHasParen: lhsHasParen,
-			}
+			}, false
 		}
 		return &ast.LambdaExpr{
 			First:       first,
@@ -2135,26 +2162,31 @@ func (p *parser) parseLambdaExpr(allowCmd, allowRangeExpr bool) ast.Expr {
 			Rhs:         rhs,
 			LhsHasParen: lhsHasParen,
 			RhsHasParen: rhsHasParen,
-		}
-	}
-	if _, ok := x.(*tupleExpr); ok {
-		panic("TODO: tupleExpr")
+		}, false
+	} else if isTuple && !allowTuple {
+		p.error(x.(*tupleExpr).opening, msgTupleNotSupported)
+		p.advance(stmtStart)
 	}
-	return x
+	return
 }
 
 // If lhs is set and the result is an identifier, it is not resolved.
 // The result may be a type or even a raw type ([...]int). Callers must
 // check the result (using checkExpr or checkExprOrType), depending on
 // context.
-func (p *parser) parseExpr(lhs, allowCmd, allowRangeExpr bool) ast.Expr {
+func (p *parser) parseExprEx(lhs, allowTuple, allowCmd, allowRangeExpr bool) (ast.Expr, bool) {
 	if p.trace {
 		defer un(trace(p, "Expression"))
 	}
 	if lhs {
 		return p.parseBinaryExpr(true, token.LowestPrec+1, false, allowCmd)
 	}
-	return p.parseLambdaExpr(allowCmd, allowRangeExpr)
+	return p.parseLambdaExpr(allowTuple, allowCmd, allowRangeExpr)
+}
+
+func (p *parser) parseExpr(lhs, allowCmd, allowRangeExpr bool) ast.Expr {
+	x, _ := p.parseExprEx(lhs, false, allowCmd, allowRangeExpr)
+	return x
 }
 
 func (p *parser) parseRHS() ast.Expr {
@@ -2169,11 +2201,19 @@ func (p *parser) parseRHSEx(allowRangeExpr bool) ast.Expr {
 	return x
 }
 
-func (p *parser) parseRHSOrType() ast.Expr {
+func (p *parser) parseRHSOrTypeEx(allowTuple bool) (x ast.Expr, isTuple bool) {
 	old := p.inRHS
 	p.inRHS = true
-	x := p.checkExprOrType(p.parseExpr(false, false, false))
+	x, isTuple = p.parseExprEx(false, allowTuple, false, false)
+	if !isTuple {
+		x = p.checkExprOrType(x)
+	}
 	p.inRHS = old
+	return
+}
+
+func (p *parser) parseRHSOrType() ast.Expr {
+	x, _ := p.parseRHSOrTypeEx(false)
 	return x
 }
 


Test patch:
diff --git a/cl/compile_test.go b/cl/compile_test.go
index 989d5506..30828d27 100644
--- a/cl/compile_test.go
+++ b/cl/compile_test.go
@@ -137,6 +137,34 @@ ls
 `)
 }
 
+func TestFileEnumLines(t *testing.T) {
+	gopClTest(t, `
+import "os"
+
+for line <- os.Stdin {
+	println line
+}
+`, `package main
+
+import (
+	fmt "fmt"
+	os "os"
+	iox "github.com/goplus/gop/builtin/iox"
+)
+
+func main() {
+	for _gop_it := iox.EnumLines(os.Stdin); ; {
+		var _gop_ok bool
+		line, _gop_ok := _gop_it.Next()
+		if !_gop_ok {
+			break
+		}
+		fmt.Println(line)
+	}
+}
+`)
+}
+
 func TestMixedGo(t *testing.T) {
 	gopMixedClTest(t, "main", `package main
 
diff --git a/cmd/internal/test/test.go b/cmd/internal/test/test.go
index c6bfb915..aa8cc042 100644
--- a/cmd/internal/test/test.go
+++ b/cmd/internal/test/test.go
@@ -35,20 +35,21 @@ import (
 
 // gop test
 var Cmd = &base.Command{
-	UsageLine: "gop test [-v] [packages]",
+	UsageLine: "gop test [-debug] [packages]",
 	Short:     "Test Go+ packages",
 }
 
 var (
-	flag        = &Cmd.Flag
-	flagVerbose = flag.Bool("v", false, "print verbose information")
+	flag      = &Cmd.Flag
+	flagDebug = flag.Bool("debug", false, "print debug information")
 )
 
 func init() {
 	Cmd.Run = runCmd
 }
 
-func runCmd(_ *base.Command, args []string) {
+func runCmd(cmd *base.Command, args []string) {
+	pass := PassTestFlags(cmd)
 	err := flag.Parse(args)
 	if err != nil {
 		log.Fatalln("parse input arguments failed:", err)
@@ -64,7 +65,7 @@ func runCmd(_ *base.Command, args []string) {
 		log.Panicln("gopprojs.ParseAll:", err)
 	}
 
-	if *flagVerbose {
+	if *flagDebug {
 		gox.SetDebug(gox.DbgFlagAll &^ gox.DbgFlagComments)
 		cl.SetDebug(cl.DbgFlagAll)
 		cl.SetDisableRecover(true)
@@ -73,6 +74,7 @@ func runCmd(_ *base.Command, args []string) {
 	gopEnv := gopenv.Get()
 	conf := &gop.Config{Gop: gopEnv}
 	confCmd := &gocmd.Config{Gop: gopEnv}
+	confCmd.Flags = pass.Args
 	for _, proj := range projs {
 		test(proj, conf, confCmd)
 	}
diff --git a/cmd/internal/test/testflag.go b/cmd/internal/test/testflag.go
new file mode 100644
index 00000000..ddd2e536
--- /dev/null
+++ b/cmd/internal/test/testflag.go
@@ -0,0 +1,58 @@
+package test
+
+import (
+	"github.com/goplus/gop/cmd/internal/base"
+)
+
+type boolFlag interface {
+	IsBoolFlag() bool
+}
+
+func PassTestFlags(cmd *base.Command) *base.PassArgs {
+	p := base.PassBuildFlags(cmd)
+	p.Bool("c", "i", "cover", "json", "benchmem", "failfast", "short")
+	p.Var("o", "covermode", "coverpkg", "exec", "vet",
+		"bench", "benchtime", "blockprofile", "blockprofilerate",
+		"count", "coverprofile", "cpu", "cpuprofile",
+		"fuzz", "list", "memprofile", "memprofilerate",
+		"mutexprofile", "mutexprofilefraction", "outputdir", "parallel",
+		"run", "timeout", "fuzztime", "fuzzminimizetime",
+		"trace", "shuffle")
+	for name := range passFlagToTest {
+		if b, ok := cmd.Flag.Lookup(name).Value.(boolFlag); ok && b.IsBoolFlag() {
+			p.Bool("test." + name)
+		} else {
+			p.Var("test." + name)
+		}
+	}
+	return p
+}
+
+var passFlagToTest = map[string]bool{
+	"bench":                true,
+	"benchmem":             true,
+	"benchtime":            true,
+	"blockprofile":         true,
+	"blockprofilerate":     true,
+	"count":                true,
+	"coverprofile":         true,
+	"cpu":                  true,
+	"cpuprofile":           true,
+	"failfast":             true,
+	"fuzz":                 true,
+	"fuzzminimizetime":     true,
+	"fuzztime":             true,
+	"list":                 true,
+	"memprofile":           true,
+	"memprofilerate":       true,
+	"mutexprofile":         true,
+	"mutexprofilefraction": true,
+	"outputdir":            true,
+	"parallel":             true,
+	"run":                  true,
+	"short":                true,
+	"shuffle":              true,
+	"timeout":              true,
+	"trace":                true,
+	"v":                    true,
+}
diff --git a/parser/_testdata/cmdlinestyle1/cmd.gop b/parser/_testdata/cmdlinestyle1/cmd.gop
index 3da8f4c4..f6d42c61 100644
--- a/parser/_testdata/cmdlinestyle1/cmd.gop
+++ b/parser/_testdata/cmdlinestyle1/cmd.gop
@@ -1,10 +1,1 @@
-package main
-
-func main() {
-	changeYpos -0.7, 8
-	changeYpos -0.7
-	changeYpos - 0.7
-	changeYpos-0.7
-	x[1]
-	x []...
-}
+println (1+2i)*2
diff --git a/parser/_testdata/cmdlinestyle1/parser.expect b/parser/_testdata/cmdlinestyle1/parser.expect
index 2573a7b0..519b39fa 100644
--- a/parser/_testdata/cmdlinestyle1/parser.expect
+++ b/parser/_testdata/cmdlinestyle1/parser.expect
@@ -1,6 +1,7 @@
 package main
 
 file cmd.gop
+noEntrypoint
 ast.FuncDecl:
   Name:
     ast.Ident:
@@ -17,67 +18,24 @@ ast.FuncDecl:
             ast.CallExpr:
               Fun:
                 ast.Ident:
-                  Name: changeYpos
+                  Name: println
               Args:
-                ast.UnaryExpr:
-                  Op: -
+                ast.BinaryExpr:
                   X:
+                    ast.ParenExpr:
+                      X:
+                        ast.BinaryExpr:
+                          X:
+                            ast.BasicLit:
+                              Kind: INT
+                              Value: 1
+                          Op: +
+                          Y:
+                            ast.BasicLit:
+                              Kind: IMAG
+                              Value: 2i
+                  Op: *
+                  Y:
                     ast.BasicLit:
-                      Kind: FLOAT
-                      Value: 0.7
-                ast.BasicLit:
-                  Kind: INT
-                  Value: 8
-        ast.ExprStmt:
-          X:
-            ast.CallExpr:
-              Fun:
-                ast.Ident:
-                  Name: changeYpos
-              Args:
-                ast.UnaryExpr:
-                  Op: -
-                  X:
-                    ast.BasicLit:
-                      Kind: FLOAT
-                      Value: 0.7
-        ast.ExprStmt:
-          X:
-            ast.BinaryExpr:
-              X:
-                ast.Ident:
-                  Name: changeYpos
-              Op: -
-              Y:
-                ast.BasicLit:
-                  Kind: FLOAT
-                  Value: 0.7
-        ast.ExprStmt:
-          X:
-            ast.BinaryExpr:
-              X:
-                ast.Ident:
-                  Name: changeYpos
-              Op: -
-              Y:
-                ast.BasicLit:
-                  Kind: FLOAT
-                  Value: 0.7
-        ast.ExprStmt:
-          X:
-            ast.IndexExpr:
-              X:
-                ast.Ident:
-                  Name: x
-              Index:
-                ast.BasicLit:
-                  Kind: INT
-                  Value: 1
-        ast.ExprStmt:
-          X:
-            ast.CallExpr:
-              Fun:
-                ast.Ident:
-                  Name: x
-              Args:
-                ast.SliceLit:
+                      Kind: INT
+                      Value: 2
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 9ece8b55..c8995720 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -50,12 +50,18 @@ func testErrCode(t *testing.T, code string, errExp, panicExp string) {
 	}
 }
 
+func TestErrTuple(t *testing.T) {
+	testErrCode(t, `println (1,2)*2`, `/foo/bar.gop:1:9: tuple is not supported`, ``)
+	testErrCode(t, `println 2*(1,2)`, `/foo/bar.gop:1:13: expected ')', found ','`, ``)
+	testErrCode(t, `func test() (int,int) { return (100,100)`, `/foo/bar.gop:1:32: tuple is not supported`, ``)
+}
+
 func TestErrOperand(t *testing.T) {
 	testErrCode(t, `a :=`, `/foo/bar.gop:1:5: expected operand, found 'EOF'`, ``)
 }
 
 func TestErrMissingComma(t *testing.T) {
-	testErrCode(t, `func a(b int c)`, ``, `missing ',' in parameter list`)
+	testErrCode(t, `func a(b int c)`, `/foo/bar.gop:1:14: missing ',' in parameter list`, ``)
 }
 
 func TestErrLambda(t *testing.T) {
diff --git a/parser/parserdir_test.go b/parser/parserdir_test.go
index 003053a5..99f2016a 100644
--- a/parser/parserdir_test.go
+++ b/parser/parserdir_test.go
@@ -175,13 +175,12 @@ func TestErrParse(t *testing.T) {
 	}
 }
 
-func TestFromTestdata(t *testing.T) {
-	sel := ""
+func testFromDir(t *testing.T, sel, relDir string) {
 	dir, err := os.Getwd()
 	if err != nil {
 		t.Fatal("Getwd failed:", err)
 	}
-	dir = path.Join(dir, "./_testdata")
+	dir = path.Join(dir, relDir)
 	fis, err := ioutil.ReadDir(dir)
 	if err != nil {
 		t.Fatal("ReadDir failed:", err)
@@ -197,4 +196,12 @@ func TestFromTestdata(t *testing.T) {
 	}
 }
 
+func TestFromTestdata(t *testing.T) {
+	testFromDir(t, "", "./_testdata")
+}
+
+func TestFromNofmt(t *testing.T) {
+	testFromDir(t, "", "./_nofmt")
+}
+
 // -----------------------------------------------------------------------------
diff --git a/printer/gop_test.go b/printer/gop_test.go
index 0440e7d9..70293574 100644
--- a/printer/gop_test.go
+++ b/printer/gop_test.go
@@ -182,9 +182,6 @@ func TestFromParse(t *testing.T) {
 			return err
 		}
 		name := info.Name()
-		if name == "cmd.gop" { // skip this file
-			return nil
-		}
 		if !info.IsDir() && filepath.Ext(name) == ".gop" {
 			testFrom(t, path, sel, 0)
 		}