Repository: goplus/gop
Pull request #1291 (2022-06-18 15:46:53)

v1.1.1


Patch:
diff --git a/Makefile b/Makefile
index e561acab..bddf0767 100644
--- a/Makefile
+++ b/Makefile
@@ -10,26 +10,25 @@ clean:
 	rm -f bin/*
 
 build:
-	go run cmd/make.go -install
+	go run cmd/make.go -build
 
 dist:
 	$(MAKE) clean
 	mkdir -p bin/
-	go build -o $(BUILD_ROOT_DIR)/install cmd/make.go
+	go build -o $(BUILD_ROOT_DIR)/make cmd/make.go
 	$(MAKE) build-all
 
 build-all: darwin-amd64.zip darwin-arm64.zip linux-386.zip linux-amd64.zip \
 	linux-armv7.zip windows-386.zip windows-amd64.zip windows-armv7.zip windows-arm64.zip
 
 build-dist:
-	$(BUILD_ROOT_DIR)/install -install
+	@mkdir -p bin/
+	@rm -rf bin/*
+	$(BUILD_ROOT_DIR)/make -build
 
 %.zip: %
 	@echo "Building $(NAME)-$(RELEASE_VERSION)-$@"
 
-	@mkdir -p bin/
-	@rm -rf bin/*
-	$(BUILD_ROOT_DIR)/install -install
 	@rm -f $(BUILD_ROOT_DIR)/$(NAME)-$(RELEASE_VERSION)-$@
 	zip -r $(BUILD_ROOT_DIR)/$(NAME)-$(RELEASE_VERSION)-$@ . -x ".*" -x "*/.*" -x "$(BUILD_ROOT_DIR)/*"
 	@echo "$(NAME)-$(RELEASE_VERSION)-$@ Done"
diff --git a/README.md b/README.md
index b13cf6a5..85bfc364 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,51 @@
-## The Go+ language for engineering, STEM education, and data science
+<div align="center">
+<p></p>
+<p>
+    <img width="80" src="https://goplus.org/favicon.svg">
+</p>
+<h1>The Go+ Programming Language</h1>
 
-[![Build Status](https://github.com/goplus/gop/actions/workflows/go.yml/badge.svg)](https://github.com/goplus/gop/actions/workflows/go.yml)
+[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)
+
+</div>
+
+<div align="center">
+<!--
+[![VSCode](https://img.shields.io/badge/vscode-Go+-teal.svg)](https://github.com/gopcode/vscode-goplus)
+[![Discord](https://img.shields.io/discord/983646982100897802?label=Discord&logo=discord&logoColor=white)](https://discord.gg/mYjWCJDcAr)
+[![Interpreter](https://img.shields.io/badge/interpreter-iGo+-seagreen.svg)](https://github.com/goplus/igop)
+-->
+
+[![Build Status](https://github.com/goplus/gox/actions/workflows/go.yml/badge.svg)](https://github.com/goplus/gox/actions/workflows/go.yml)
 [![Go Report Card](https://goreportcard.com/badge/github.com/goplus/gop)](https://goreportcard.com/report/github.com/goplus/gop)
 [![Coverage Status](https://codecov.io/gh/goplus/gop/branch/main/graph/badge.svg)](https://codecov.io/gh/goplus/gop)
 [![GitHub release](https://img.shields.io/github/v/tag/goplus/gop.svg?label=release)](https://github.com/goplus/gop/releases)
-[![Tutorials](https://img.shields.io/badge/tutorial-Go+-blue.svg)](https://tutorial.goplus.org/)
-[![Playground](https://img.shields.io/badge/play-Go+-blue.svg)](https://play.goplus.org/)
-[![VSCode](https://img.shields.io/badge/vscode-Go+-teal.svg)](https://github.com/gopcode/vscode-goplus)
-[![Interpreter](https://img.shields.io/badge/interpreter-iGo+-seagreen.svg)](https://github.com/goplus/igop)
+[![Discord](https://img.shields.io/badge/Discord-online-success.svg?logo=discord&logoColor=white)](https://discord.gg/mYjWCJDcAr)
+
+</div>
+
+The Go+ programming language is designed for engineering, STEM education, and data science.
 
 * **For engineering**: working in the simplest language that can be mastered by children.
 * **For STEM education**: studying an engineering language that can be used for work in the future.
 * **For data science**: communicating with engineers in the same language.
 
-## Go vs. Go+
-
-What's the difference between Go and Go+?
-
-The goal of Go is to provide a way to easily build simple, reliable, and efficient software. It wants to be a better C.
-
-The goal of Go+ is to achieve a natural fusion of engineering and low-code. It wants to be a better Python.
+For more details, see [Quick Start](doc/docs.md).
 
-The essence of low-code is non-professionals oriented. Today's mainstream programming language designers still take non-professionals as a niche group. But Go+ take this group as vital, and they are a vital breakthrough in today's programming language revolution.
+## Key Features of Go+
 
-The Python language appeared very early (1991), but its design idea is very far-sighted, and it is a rare non-professionals oriented programming language so far. There are quite a few languages that look relatively concise, such as Ruby and CoffeeScript, but they have many language magics so that people feel they are flexible and powerful, but not easy to master. They cannot be regarded as non-professionals oriented.
-
-Python is very popular today, but it's hard to call it an engineering language. Most people just take it as a tool for rapid prototyping.
-
-Is it possible to minimize the complexity of engineering and present it in a low-code form? Go+ hopes to explore a new path at this point.
+* A static typed language.
+* The simplest engineering language that can be mastered by children (script-like style).
+* 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)).
+* REPL: see [iGo+](https://github.com/goplus/igop).
+* [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).
 
 ## How to install
 
+### from source code
+
 For now, we suggest you install Go+ from source code.
 
 Note: Requires go1.16 or later
@@ -45,1273 +60,36 @@ cd gop
 all.bat
 ```
 
-Actually, `all.bash` and `all.bat` will use `go run cmd/make.go` underneath.
-
-
-## Running in Go+ playground
-
-If you don't want install Go+, you can write your Go+ programs in Go+ playground. This is the fastest way to experience Go+.
-
-* Go+ playground based on Docker: https://play.goplus.org/
-* Go+ playground based on GopherJS: https://jsplay.goplus.org/
-
-And you can share your Go+ code with your friends.
-Here is my `Hello world` program:
-* https://play.goplus.org/p/AAh_gQAKAZR.
-
-
-## Table of Contents
-
-<table>
-    <tr><td width=33% valign=top>
-
-* [Hello world](#hello-world)
-* [Running a project folder](#running-a-project-folder-with-several-files)
-* [Comments](#comments)
-* [Variables](#variables)
-* [Go+ types](#go-types)
-    * [Strings](#strings)
-    * [Numbers](#numbers)
-    * [Slices](#slices)
-    * [Maps](#maps)
-* [Module imports](#module-imports)
-
-</td><td width=33% valign=top>
-
-* [Statements & expressions](#statements--expressions)
-    * [If..else](#ifelse)
-    * [For loop](#for-loop)
-    * [Error handling](#error-handling)
-* [Functions](#functions)
-    * [Returning multiple values](#returning-multiple-values)
-    * [Variadic parameters](#variadic-parameters)
-    * [Higher order functions](#higher-order-functions)
-    * [Lambda expressions](#lambda-expressions)
-* [Structs](#structs)
-
-</td><td valign=top>
-
-* [Go/Go+ hybrid programming](#gogo-hybrid-programming)
-* [Calling C from Go+](#calling-c-from-go)
-* [Data processing](#data-processing)
-    * [List comprehension](#list-comprehension)
-    * [Select data from a collection](#select-data-from-a-collection)
-    * [Check if data exists in a collection](#check-if-data-exists-in-a-collection)
-* [Unix shebang](#unix-shebang)
-* [Compatibility with Go](#compatibility-with-go)
-
-</td></tr>
-</table>
-
-
-## Hello World
-
-```go
-println "Hello world"
-```
-
-Save this snippet into a file named `hello.gop`. Now do: `gop run hello.gop`.
-
-Congratulations - you just wrote and executed your first Go+ program!
-
-You can compile a program without execution with `gop build hello.gop`.
-See `gop help` for all supported commands.
-
-[`println`](#println) is one of the few [built-in functions](#builtin-functions).
-It prints the value passed to it to standard output.
-
-See https://tutorial.goplus.org/hello-world for more details.
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-## Running a project folder with several files
-
-Suppose you have a folder with several .gop files in it, and you want 
-to compile them all into one program. Just do: `gop run .`.
-
-Passing parameters also works, so you can do:
-`gop run . --yourparams some_other_stuff`.
-
-Your program can then use the CLI parameters like this:
-
-```go
-import "os"
-
-println os.Args
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-## Comments
-
-```go
-# This is a single line comment.
-
-// This is a single line comment.
-
-/*
-This is a multiline comment.
-*/
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-## Variables
-
-```go
-name := "Bob"
-age := 20
-largeNumber := int128(1 << 65)
-println name, age
-println largeNumber
-```
-
-Variables are declared and initialized with `:=`.
-
-The variable's type is inferred from the value on the right hand side.
-To choose a different type, use type conversion:
-the expression `T(v)` converts the value `v` to the
-type `T`.
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-### Initialization vs. assignment
-
-Note the (important) difference between `:=` and `=`.
-`:=` is used for declaring and initializing, `=` is used for assigning.
-
-```go failcompile
-age = 21
-```
-
-This code will not compile, because the variable `age` is not declared.
-All variables need to be declared in Go+.
-
-```go
-age := 21
-```
-
-The values of multiple variables can be changed in one line.
-In this way, their values can be swapped without an intermediary variable.
-
-```go
-a, b := 0, 1
-a, b = b, a
-println a, b // 1, 0
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-## Go+ Types
-
-### Primitive types
-
-```go ignore
-bool
-
-int8    int16   int32   int    int64    int128
-uint8   uint16  uint32  uint   uint64   uint128
-
-uintptr // similar to C's size_t
-
-byte // alias for uint8
-rune // alias for int32, represents a Unicode code point
-
-string
-
-float32 float64
-
-complex64 complex128
-
-bigint bigrat
-
-unsafe.Pointer // similar to C's void*
-
-any // alias for Go's interface{}
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-### Strings
-
-```go
-name := "Bob"
-println name.len  // 3
-println name[0]   // 66
-println name[1:3] // ob
-println name[:2]  // Bo
-println name[2:]  // b
-
-// or using octal escape `\###` notation where `#` is an octal digit
-println "\141a"   // aa
-
-// Unicode can be specified directly as `\u####` where # is a hex digit
-// and will be converted internally to its UTF-8 representation
-println "\u2605"  // ★
-```
-
-String values are immutable. You cannot mutate elements:
-
-```go failcompile
-s := "hello 🌎"
-s[0] = `H` // not allowed
-```
-
-Note that indexing a string will produce a `byte`, not a `rune` nor another `string`.
-
-Strings can be easily converted to integers:
-
-```go
-s := "12"
-a, err := s.int
-b := s.int! // will panic if s isn't a valid integer
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-#### String operators
-
-```go
-name := "Bob"
-bobby := name + "by" // + is used to concatenate strings
-println bobby // Bobby
-
-s := "Hello "
-s += "world"
-println s // Hello world
-```
-
-All operators in Go+ must have values of the same type on both sides. You cannot concatenate an
-integer to a string:
-
-```go failcompile
-age := 10
-println "age = " + age // not allowed
-```
-
-We have to either convert `age` to a `string`:
-
-```go
-age := 10
-println "age = " + age.string
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### Runes
-
-A `rune` represents a single Unicode character and is an alias for `int32`.
-
-```go
-rocket := '🚀'
-println rocket         // 128640
-println string(rocket) // 🚀
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### Numbers
-
-```go
-a := 123
-```
-
-This will assign the value of 123 to `a`. By default `a` will have the
-type `int`.
-
-You can also use hexadecimal, binary or octal notation for integer literals:
-
-```go
-a := 0x7B
-b := 0b01111011
-c := 0o173
-```
-
-All of these will be assigned the same value, 123. They will all have type
-`int`, no matter what notation you used.
-
-Go+ also supports writing numbers with `_` as separator:
-
-```go
-num := 1_000_000 // same as 1000000
-three := 0b0_11 // same as 0b11
-floatNum := 3_122.55 // same as 3122.55
-hexa := 0xF_F // same as 255
-oct := 0o17_3 // same as 0o173
-```
-
-If you want a different type of integer, you can use casting:
-
-```go
-a := int64(123)
-b := uint8(12)
-c := int128(12345)
-```
-
-Assigning floating point numbers works the same way:
-
-```go
-f1 := 1.0
-f2 := float32(3.14)
-```
-
-If you do not specify the type explicitly, by default float literals will have the type of `float64`.
-
-Float literals can also be declared as a power of ten:
-
-```go
-f0 := 42e1   // 420
-f1 := 123e-2 // 1.23
-f2 := 456e+2 // 45600
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-#### Rational numbers
-
-We introduce rational numbers as native Go+ types. We use suffix `r` to denote rational literals. For example, `1r << 200` means a big int whose value is equal to 2<sup>200</sup>.
-
-```go
-a := 1r << 200
-b := bigint(1 << 200)
-```
-
-By default, `1r` will have the type of `bigint`.
-
-And `4/5r` means the rational constant `4/5`.
-It will have the type of `bigrat`.
-
-```go
-a := 4/5r
-b := a - 1/3r + 3 * 1/2r
-println a, b // 4/5 59/30
-```
-
-Casting rational numbers works the same way:
-
-```go
-a := 1r
-b := bigrat(1r)
-c := bigrat(1)
-println a/3 // 0
-println b/3 // 1/3
-println c/3 // 1/3
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-#### Convert bool to number types
-
-```go
-println int(true)       // 1
-println float64(true)   // 1
-println complex64(true) // (1+0i)
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### Slices
-
-A slice is a collection of data elements of the same type. A slice literal is a
-list of expressions surrounded by square brackets. An individual element can be
-accessed using an *index* expression. Indexes start from `0`:
-
-```go
-nums := [1, 2, 3]
-println nums      // [1 2 3]
-println nums.len  // 3
-println nums[0]   // 1
-println nums[1:3] // [2 3]
-println nums[:2]  // [1 2]
-println nums[2:]  // [3]
-
-nums[1] = 5
-println nums // [1 5 3]
-```
-
-Type of a slice literal is infered automatically.
-
-```go
-a := [1, 2, 3]   // []int
-b := [1, 2, 3.4] // []float64
-c := ["Hi"]      // []string
-d := ["Hi", 10]  // []any
-d := []          // []any
-```
-
-And casting slices also works.
-
-```go
-a := []float64([1, 2, 3]) // []float64
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### Maps
-
-```go
-a := {"Hello": 1, "xsw": 3}     // map[string]int
-b := {"Hello": 1, "xsw": 3.4}   // map[string]float64
-c := {"Hello": 1, "xsw": "Go+"} // map[string]any
-d := {}                         // map[string]any
-```
-
-If a key is not found, a zero value is returned by default:
-
-```go
-a := {"Hello": 1, "xsw": 3}
-c := {"Hello": 1, "xsw": "Go+"}
-println a["bad_key"] // 0
-println c["bad_key"] // <nil>
-```
-
-You can also check, if a key is present, and get its value.
-
-```go
-a := {"Hello": 1, "xsw": 3}
-if v, ok := a["xsw"]; ok {
-    println "its value is", v
-}
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-## Module imports
-
-For information about creating a module, see [Modules](#modules).
-
-Modules can be imported using the `import` keyword:
-
-```go
-import "strings"
-
-x := strings.NewReplacer("?", "!").Replace("Hello, world???")
-println x // Hello, world!!!
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### Module import aliasing
-
-Any imported module name can be aliased:
+### on macOS
 
-```go
-import strop "strings"
-
-x := strop.NewReplacer("?", "!").Replace("Hello, world???")
-println x // Hello, world!!!
+```sh
+brew install goplus
 ```
 
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-## Statements & expressions
-
-
-### If..else
-
-In Go+, `if` statements are pretty straightforward and similar to most other languages.
-Unlike other C-like languages,
-there are no parentheses surrounding the condition and the braces are always required.
-
-```go
-a := 10
-b := 20
-if a < b {
-	println "a < b"
-} else if a > b {
-	println "a > b"
-} else {
-	println "a == b"
-}
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### For loop
-
-Go+ has only one looping keyword: `for`, with several forms.
-
-#### `for`/`<-`
-
-This is the most common form. You can use it with a slice, map, numeric range or custom iterators.
-
-For information about creating a custom iterators, see [Custom iterators](#custom-iterators).
-
-##### Slice `for`
-
-The `for value <- arr` form is used for going through elements of a slice.
-
-```go
-numbers := [1, 3, 5, 7, 11, 13, 17]
-sum := 0
-for x <- numbers {
-    sum += x
-}
-println sum // 57
-```
+## Go+ Applications
 
-If an index is required, an alternative form `for index, value <- arr` can be used.
+### 2D Games powered by Go+
 
-```go
-names := ["Sam", "Peter"]
-for i, name <- names {
-    println i, name
-    // 0 Sam
-    // 1 Peter
-}
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-##### Map `for`
-
-```go
-m := {"one": 1, "two": 2}
-for key, val <- m {
-	println key, val
-	// one 1
-	// two 2
-}
-for key, _ <- m {
-	println key
-	// one
-	// two
-}
-for val <- m {
-	println val
-	// 1
-	// 2
-}
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-##### Range `for`
-
-You can use `range expression` (`start:end:step`) in for loop.
-
-```go
-for i <- :5 {
-    println i
-    // 0
-    // 1
-    // 2
-    // 3
-    // 4
-}
-for i <- 1:5 {
-    println i
-    // 1
-    // 2
-    // 3
-    // 4
-}
-for i <- 1:5:2 {
-    println i
-    // 1
-    // 3
-}
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-##### `for`/`<-`/`if`
-
-All loops of `for`/`<-` form can have an optional `if` condition.
-
-```go
-numbers := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-for num <- numbers if num%3 == 0 {
-    println num
-    // 0
-    // 3
-    // 6
-    // 9
-}
-
-for num <- :10 if num%3 == 0 {
-    println num
-    // 0
-    // 3
-    // 6
-    // 9
-}
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-#### Condition `for`
-
-```go
-sum := 0
-i := 1
-for i <= 100 {
-	sum += i
-	i++
-}
-println sum // 5050
-```
-
-This form of the loop is similar to `while` loops in other languages.
-The loop will stop iterating once the boolean condition evaluates to false.
-Again, there are no parentheses surrounding the condition, and the braces are always required.
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-#### C `for`
-
-```go
-for i := 0; i < 10; i += 2 {
-    // Don't print 6
-    if i == 6 {
-        continue
-    }
-    println i
-    // 0
-    // 2
-    // 4
-    // 8
-}
-```
+* [A Go+ 2D Game Engine for STEM education](https://github.com/goplus/spx)
+* [Aircraft War](https://github.com/goplus/AircraftWar)
+* [Flappy Bird](https://github.com/goplus/FlappyCalf)
+* [Maze Play](https://github.com/goplus/MazePlay)
+* [BetaGo](https://github.com/xushiwei/BetaGo)
+* [Gobang](https://github.com/xushiwei/Gobang)
+* [Dinosaur](https://github.com/xushiwei/Dinosaur)
 
-Finally, there's the traditional C style `for` loop. It's safer than the `while` form
-because with the latter it's easy to forget to update the counter and get
-stuck in an infinite loop.
+### Data processing
 
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-#### Bare `for`
-
-```go
-for {
-    // ...
-}
-```
-
-The condition can be omitted, resulting in an infinite loop. You can use `break` or `return` to end the loop.
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### Error handling
-
-We reinvent the error handling specification in Go+. We call them `ErrWrap expressions`:
-
-```go
-expr! // panic if err
-expr? // return if err
-expr?:defval // use defval if err
-```
-
-How to use them? Here is an example:
-
-```go
-import (
-    "strconv"
-)
-
-func add(x, y string) (int, error) {
-    return strconv.Atoi(x)? + strconv.Atoi(y)?, nil
-}
-
-func addSafe(x, y string) int {
-    return strconv.Atoi(x)?:0 + strconv.Atoi(y)?:0
-}
-
-println `add("100", "23"):`, add("100", "23")!
-
-sum, err := add("10", "abc")
-println `add("10", "abc"):`, sum, err
-
-println `addSafe("10", "abc"):`, addSafe("10", "abc")
-```
-
-The output of this example is:
-
-```
-add("100", "23"): 123
-add("10", "abc"): 0 strconv.Atoi: parsing "abc": invalid syntax
-
-===> errors stack:
-main.add("10", "abc")
-    /Users/xsw/tutorial/15-ErrWrap/err_wrap.gop:6 strconv.Atoi(y)?
-
-addSafe("10", "abc"): 10
-```
-
-Compared to corresponding Go code, It is clear and more readable.
-
-And the most interesting thing is, the return error contains the full error stack. When we got an error, it is very easy to position what the root cause is.
-
-How these `ErrWrap expressions` work? See [Error Handling](https://github.com/goplus/gop/wiki/Error-Handling) for more information.
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-## Functions
-
-```go
-func add(x int, y int) int {
-	return x + y
-}
-
-println add(2, 3) // 5
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### Returning multiple values
-
-```go
-func foo() (int, int) {
-	return 2, 3
-}
-
-a, b := foo()
-println a // 2
-println b // 3
-c, _ := foo() // ignore values using `_`
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### Variadic parameters
-
-```go
-func sum(a ...int) int {
-    total := 0
-	for x <- a {
-		total += x
-	}
-	return total
-}
-
-println sum(2, 3, 5) // 10
-```
-
-Output parameters can have names.
-
-```go
-func sum(a ...int) (total int) {
-	for x <- a {
-		total += x
-	}
-	return // don't need return values if they are assigned
-}
-
-println sum(2, 3, 5) // 10
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### Higher order functions
-
-Functions can also be parameters.
-
-```go
-func square(x float64) float64 {
-    return x*x
-}
-
-func abs(x float64) float64 {
-    if x < 0 {
-        return -x
-    }
-    return x
-}
-
-func transform(a []float64, f func(float64) float64) []float64 {
-    return [f(x) for x <- a]
-}
-
-y := transform([1, 2, 3], square)
-println y // [1 4 9]
-
-z := transform([-3, 1, -5], abs)
-println z // [3 1 5]
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### Lambda expressions
-
-You also can use `lambda expression` to define a anonymous function.
-
-```go
-func transform(a []float64, f func(float64) float64) []float64 {
-    return [f(x) for x <- a]
-}
-
-y := transform([1, 2, 3], x => x*x)
-println y // [1 4 9]
-
-z := transform([-3, 1, -5], x => {
-    if x < 0 {
-        return -x
-    }
-    return x
-})
-println z // [3 1 5]
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-## Structs
-
-### Custom iterators
-
-#### For range of UDT
-
-```go
-type Foo struct {
-}
-
-// Gop_Enum(proc func(val ValType)) or:
-// Gop_Enum(proc func(key KeyType, val ValType))
-func (p *Foo) Gop_Enum(proc func(key int, val string)) {
-    // ...
-}
-
-foo := &Foo{}
-for k, v := range foo {
-    println k, v
-}
-
-for k, v <- foo {
-    println k, v
-}
-
-println {v: k for k, v <- foo}
-```
-
-**Note: you can't use break/continue or return statements in for range of udt.Gop_Enum(callback).**
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-#### For range of UDT2
-
-```go
-type FooIter struct {
-}
-
-// (Iterator) Next() (val ValType, ok bool) or:
-// (Iterator) Next() (key KeyType, val ValType, ok bool)
-func (p *FooIter) Next() (key int, val string, ok bool) {
-    // ...
-}
-
-type Foo struct {
-}
-
-// Gop_Enum() Iterator
-func (p *Foo) Gop_Enum() *FooIter {
-    // ...
-}
-
-foo := &Foo{}
-for k, v := range foo {
-    println k, v
-}
-
-for k, v <- foo {
-    println k, v
-}
-
-println {v: k for k, v <- foo}
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### Deduce struct type
-
-```go
-type Config struct {
-    Dir   string
-    Level int
-}
-
-func foo(conf *Config) {
-    // ...
-}
-
-foo {Dir: "/foo/bar", Level: 1}
-```
-
-Here `foo {Dir: "/foo/bar", Level: 1}` is equivalent to `foo(&Config{Dir: "/foo/bar", Level: 1})`. However, you can't replace `foo(&Config{"/foo/bar", 1})` with `foo {"/foo/bar", 1}`, because it is confusing to consider `{"/foo/bar", 1}` as a struct literal.
-
-You also can omit struct types in a return statement. For example:
-
-```go
-type Result struct {
-    Text string
-}
-
-func foo() *Result {
-    return {Text: "Hi, Go+"} // return &Result{Text: "Hi, Go+"}
-}
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### Overload operators
-
-```go
-import "math/big"
-
-type MyBigInt struct {
-    *big.Int
-}
-
-func Int(v *big.Int) MyBigInt {
-    return MyBigInt{v}
-}
-
-func (a MyBigInt) + (b MyBigInt) MyBigInt { // binary operator
-    return MyBigInt{new(big.Int).Add(a.Int, b.Int)}
-}
-
-func (a MyBigInt) += (b MyBigInt) {
-    a.Int.Add(a.Int, b.Int)
-}
-
-func -(a MyBigInt) MyBigInt { // unary operator
-    return MyBigInt{new(big.Int).Neg(a.Int)}
-}
-
-a := Int(1r)
-a += Int(2r)
-println a + Int(3r)
-println -a
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### Auto property
-
-Let's see an example written in Go+:
-
-```go
-import "gop/ast/goptest"
-
-doc := goptest.New(`... Go+ code ...`)!
-
-println doc.Any().FuncDecl().Name()
-```
-
-In many languages, there is a concept named `property` who has `get` and `set` methods.
-
-Suppose we have `get property`, the above example will be:
-
-```go
-import "gop/ast/goptest"
-
-doc := goptest.New(`... Go+ code ...`)!
-
-println doc.any.funcDecl.name
-```
-
-In Go+, we introduce a concept named `auto property`. It is a `get property`, but is implemented automatically. If we have a method named `Bar()`, then we will have a `get property` named `bar` at the same time.
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-## Go/Go+ hybrid programming
-
-This is an example to show how to mix Go/Go+ code in the same package.
-
-In this example, we have a Go source file named `a.go`:
-
-```go
-package main
-
-import "fmt"
-
-func p(a interface{}) {
-	sayMix()
-	fmt.Println("Hello,", a)
-}
-```
-
-And we have a Go+ source file named `b.gop`:
-
-```go
-func sayMix() {
-	println "Mix Go and Go+"
-}
-
-p "world"
-```
-
-You can see that Go calls a Go+ function named `sayMix`, and Go+ calls a Go function named `p`. As you are used to in Go programming, this kind of circular reference is allowed.
-
-Run `gop run .` to see the output of this example:
-
-```
-Mix Go and Go+
-Hello, world
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-## Calling C from Go+
-
-- The `gop c` command (equivalent to the stand-alone `c2go` command) can be used to convert a C project to a Go project.
-- `import "C"` and `import "C/xxx"` are used to import a C project converted by c2go. where `import "C"` is short for `import "C/github.com/goplus/libc"`.
-- The `C"xxx"` syntax represents C-style string constants.
-
-Here is [an example to show how Go+ interacts with C](https://github.com/goplus/gop/tree/v1.1/testdata/helloc2go).
-
-```go
-import "C"
-
-C.printf C"Hello, c2go!\n"
-C.fprintf C.stderr, C"Hi, %7.1f\n", 3.14
-```
-
-In this example we call two C standard functions `printf` and `fprintf`, passing a C variable `stderr` and two C strings in the form of `C"xxx"` (a Go+ syntax to represent C-style strings).
-
-Run `gop run .` to see the output of this example:
-
-```
-Hello, c2go!
-Hi,     3.1
-```
-
-Of course, the current Go+ support for C is only a preview version, not to the extent that it is actually available in engineering. As far as libc is concerned, the current migration progress is only about 5%, and it is just the beginning.
-
-In the upcoming Go+ v1.2 version planning, complete support for C is listed as a top priority. Of course, support for cgo and Go templates is also under planning, which is a crucial capability enhancement for Go/Go+ hybrid projects.
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-## Data processing
-
-### List comprehension
-
-```go
-a := [x*x for x <- [1, 3, 5, 7, 11]]
-b := [x*x for x <- [1, 3, 5, 7, 11] if x > 3]
-c := [i+v for i, v <- [1, 3, 5, 7, 11] if i%2 == 1]
-
-arr := [1, 2, 3, 4, 5, 6]
-d := [[a, b] for a <- arr if a < b for b <- arr if b > 2]
-
-x := {x: i for i, x <- [1, 3, 5, 7, 11]}
-y := {x: i for i, x <- [1, 3, 5, 7, 11] if i%2 == 1}
-z := {v: k for k, v <- {1: "Hello", 3: "Hi", 5: "xsw", 7: "Go+"} if k > 3}
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### Select data from a collection
-
-```go
-type student struct {
-    name  string
-    score int
-}
-
-students := [student{"Ken", 90}, student{"Jason", 80}, student{"Lily", 85}]
-
-unknownScore, ok := {x.score for x <- students if x.name == "Unknown"}
-jasonScore := {x.score for x <- students if x.name == "Jason"}
-
-println unknownScore, ok // 0 false
-println jasonScore // 80
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-### Check if data exists in a collection
-
-```go
-type student struct {
-    name  string
-    score int
-}
-
-students := [student{"Ken", 90}, student{"Jason", 80}, student{"Lily", 85}]
-
-hasJason := {for x <- students if x.name == "Jason"} // is any student named Jason?
-hasFailed := {for x <- students if x.score < 60}     // is any student failed?
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-## Unix shebang
-
-You can use Go+ programs as shell scripts now. For example:
-
-```go
-#!/usr/bin/env -S gop run
-
-println "Hello, Go+"
-
-println 1r << 129
-println 1/3r + 2/7r*2
-
-arr := [1, 3, 5, 7, 11, 13, 17, 19]
-println arr
-println [x*x for x <- arr, x > 3]
-
-m := {"Hi": 1, "Go+": 2}
-println m
-println {v: k for k, v <- m}
-println [k for k, _ <- m]
-println [v for v <- m]
-```
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-## Compatibility with Go
-
-All Go features will be supported (including partially support `cgo`, see [below](#bytecode-vs-go-code)).
-
-**All Go packages (even these packages use `cgo`) can be imported by Go+.**
-
-```coffee
-import (
-    "fmt"
-    "strings"
-)
-
-x := strings.NewReplacer("?", "!").Replace("hello, world???")
-fmt.Println "x:", x
-```
-
-**And all Go+ packages can also be imported in Go programs. What you need to do is just using `gop` command instead of `go`.**
-
-First, let's make a directory named `14-Using-goplus-in-Go`.
-
-Then write a Go+ package named [foo](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go/foo) in it:
-
-```go
-package foo
-
-func ReverseMap(m map[string]int) map[int]string {
-    return {v: k for k, v <- m}
-}
-```
-
-Then use it in a Go package [14-Using-goplus-in-Go/gomain](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go/gomain):
-
-```go
-package main
-
-import (
-    "fmt"
-
-    "github.com/goplus/tutorial/14-Using-goplus-in-Go/foo"
-)
-
-func main() {
-    rmap := foo.ReverseMap(map[string]int{"Hi": 1, "Hello": 2})
-    fmt.Println(rmap)
-}
-```
-
-How to build this example? You can use:
-
-```bash
-gop install -v ./...
-```
-
-Go [github.com/goplus/tutorial/14-Using-goplus-in-Go](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go) to get the source code.
-
-<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
-
-
-## Bytecode vs. Go code
-
-Go+ supports bytecode backend and Go code generation.
-
-When we use `gop` command, it generates Go code to covert Go+ package into Go packages.
-
-```bash
-gop run     # Run a Go+ program
-gop install # Build Go+ files and install target to GOBIN
-gop build   # Build Go+ files
-gop test    # Test Go+ packages
-gop fmt     # Format Go+ packages
-gop clean   # Clean all Go+ auto generated files
-gop go      # Convert Go+ packages into Go packages
-```
-
-When we use [`igop`](https://github.com/goplus/igop) command, it generates bytecode to execute.
-
-```bash
-igop  # Run a Go+ program
-```
-
-In bytecode mode, Go+ doesn't support `cgo`. However, in Go-code-generation mode, Go+ fully supports `cgo`.
+* [HTML DOM Query Language for Go+](https://github.com/goplus/hdq)
 
 
 ## IDE Plugins
 
-* vscode: https://github.com/gopcode/vscode-goplus
-
-
-## Tutorials
-
-* https://tutorial.goplus.org/
+* vscode: https://github.com/goplus/vscode-goplus
 
 
 ## Contributing
 
 The Go+ project welcomes all contributors. We appreciate your help!
 
-Here are [list of Go+ Contributors](https://github.com/goplus/gop/wiki/Goplus-Contributors). We award an email account (XXX@goplus.org) for every contributor. And we suggest you commit code by using this email account:
-
-```bash
-git config --global user.email XXX@goplus.org
-```
-
-If you did this, remember to add your `XXX@goplus.org` email to https://github.com/settings/emails.
-
-What does `a contributor to Go+` mean? You must meet one of the following conditions:
-
-* At least one pull request of a full-featured implemention.
-* At least three pull requests of feature enhancements.
-* At least ten pull requests of any kind issues.
-
-Where can you start?
-
-* [![Issues](https://img.shields.io/badge/ISSUEs-Go+-blue.svg)](https://github.com/goplus/gop/issues)
-* [![Issues](https://img.shields.io/badge/ISSUEs-NumGo+-blue.svg)](https://github.com/numgoplus/ng/issues)
-* [![Issues](https://img.shields.io/badge/ISSUEs-PandasGo+-blue.svg)](https://github.com/goplus/pandas/issues)
-* [![Issues](https://img.shields.io/badge/ISSUEs-vscode%20Go+-blue.svg)](https://github.com/gopcode/vscode-goplus/issues)
-* [![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/goplus/gop)](https://www.tickgit.com/browse?repo=github.com/goplus/gop)
+For more details, see [Contributing & compiler design](doc/contributing.md).
diff --git a/VERSION b/VERSION
deleted file mode 100644
index 795460fc..00000000
--- a/VERSION
+++ /dev/null
@@ -1,1 +0,0 @@
-v1.1.0
diff --git a/ast/ast.go b/ast/ast.go
index bec832a8..28e546bd 100644
--- a/ast/ast.go
+++ b/ast/ast.go
@@ -468,6 +468,11 @@ func (x *CallExpr) End() token.Pos {
 	return x.Rparen + 1
 }
 
+// IsCommand returns if a CallExpr is a command style CallExpr or not.
+func (x *CallExpr) IsCommand() bool {
+	return x.NoParenEnd != token.NoPos
+}
+
 // End returns position of first character immediately after the node.
 func (x *StarExpr) End() token.Pos { return x.X.End() }
 
diff --git a/ast/ast_gop.go b/ast/ast_gop.go
index 038e545f..9d4f717a 100644
--- a/ast/ast_gop.go
+++ b/ast/ast_gop.go
@@ -125,6 +125,7 @@ type ForPhrase struct {
 	Key, Value *Ident    // Key may be nil
 	TokPos     token.Pos // position of "<-" operator
 	X          Expr      // value to range over
+	IfPos      token.Pos // postion of if or comma; or NoPos
 	Init       Stmt      // initialization statement; or nil
 	Cond       Expr      // value filter, can be nil
 }
diff --git a/cl/builtin.go b/cl/builtin.go
index 0a5d5f15..bb06749c 100644
--- a/cl/builtin.go
+++ b/cl/builtin.go
@@ -25,14 +25,14 @@ import (
 
 // -----------------------------------------------------------------------------
 
-func initMathBig(pkg gox.PkgImporter, conf *gox.Config, big *gox.PkgRef) {
+func initMathBig(pkg *gox.Package, conf *gox.Config, big *gox.PkgRef) {
 	big.EnsureImported()
 	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, ng, buil *gox.PkgRef) {
+func initBuiltin(pkg *gox.Package, builtin *types.Package, fmt, ng, buil *gox.PkgRef) {
 	scope := builtin.Scope()
 	typs := []string{"bigint", "bigrat", "bigfloat"}
 	for _, typ := range typs {
@@ -54,7 +54,7 @@ func initBuiltin(pkg gox.PkgImporter, builtin *types.Package, fmt, ng, buil *gox
 	scope.Insert(types.NewTypeName(token.NoPos, builtin, "any", gox.TyEmptyInterface))
 }
 
-func newBuiltinDefault(pkg gox.PkgImporter, conf *gox.Config) *types.Package {
+func newBuiltinDefault(pkg *gox.Package, conf *gox.Config) *types.Package {
 	builtin := types.NewPackage("", "")
 	fmt := pkg.Import("fmt")
 	buil := pkg.Import("github.com/goplus/gop/builtin")
diff --git a/cl/classfile.go b/cl/classfile.go
index 86945a8f..432e1a2d 100644
--- a/cl/classfile.go
+++ b/cl/classfile.go
@@ -60,7 +60,7 @@ func (p *gmxSettings) getScheds(cb *gox.CodeBuilder) []goast.Stmt {
 	return p.schedStmts
 }
 
-func newGmx(pkg *gox.Package, file string, conf *Config) *gmxSettings {
+func newGmx(ctx *pkgCtx, pkg *gox.Package, file string, conf *Config) *gmxSettings {
 	_, name := filepath.Split(file)
 	ext := filepath.Ext(name)
 	if idx := strings.Index(name, "."); idx > 0 {
@@ -80,9 +80,15 @@ func newGmx(pkg *gox.Package, file string, conf *Config) *gmxSettings {
 		p.pkgImps[i] = pkg.Import(pkgPath)
 	}
 	spx := p.pkgImps[0]
-	p.game, p.gameIsPtr = spxRef(spx, "Gop_game", "Game")
+	p.game, p.gameIsPtr = spxTryRef(spx, "Gop_proj", "Proj")
+	if p.game == nil {
+		p.game, p.gameIsPtr = spxRef(spx, "Gop_game", "Game")
+	}
 	if gt.WorkExt != "" {
-		p.sprite, _ = spxRef(spx, "Gop_sprite", "Sprite")
+		p.sprite, _ = spxTryRef(spx, "Gop_work", "Work")
+		if p.sprite == nil {
+			p.sprite, _ = spxRef(spx, "Gop_sprite", "Sprite")
+		}
 	}
 	if x := getStringConst(spx, "Gop_sched"); x != "" {
 		p.scheds, p.hasScheds = strings.SplitN(x, ",", 2), true
@@ -108,13 +114,21 @@ func getDefaultClass(file string) string {
 }
 
 func spxRef(spx *gox.PkgRef, name, typ string) (obj gox.Ref, isPtr bool) {
+	obj, isPtr = spxTryRef(spx, name, typ)
+	if obj == nil {
+		panic(spx.Path() + "." + name + " not found")
+	}
+	return
+}
+
+func spxTryRef(spx *gox.PkgRef, name, typ string) (obj gox.Ref, isPtr bool) {
 	if v := getStringConst(spx, name); v != "" {
 		typ = v
 		if strings.HasPrefix(typ, "*") {
 			typ, isPtr = typ[1:], true
 		}
 	}
-	obj = spx.Ref(typ)
+	obj = spx.TryRef(typ)
 	return
 }
 
diff --git a/cl/compile.go b/cl/compile.go
index bceb1fc2..591d0537 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -18,12 +18,10 @@
 package cl
 
 import (
-	"errors"
 	"fmt"
 	"go/types"
 	"log"
 	"os"
-	"path"
 	"reflect"
 	"strings"
 
@@ -33,6 +31,7 @@ import (
 	"github.com/goplus/gox"
 	"github.com/goplus/gox/cpackages"
 	"github.com/goplus/mod/modfile"
+	"github.com/qiniu/x/errors"
 )
 
 const (
@@ -59,18 +58,6 @@ func SetDebug(flags int) {
 	debugLookup = (flags & DbgFlagLookup) != 0
 }
 
-type Errors struct {
-	Errs []error
-}
-
-func (p *Errors) Error() string {
-	msgs := make([]string, len(p.Errs))
-	for i, err := range p.Errs {
-		msgs[i] = err.Error()
-	}
-	return strings.Join(msgs, "\n")
-}
-
 // -----------------------------------------------------------------------------
 
 type Class = modfile.Classfile
@@ -255,7 +242,7 @@ type pkgCtx struct {
 	syms  map[string]loader
 	inits []func()
 	tylds []*typeLoader
-	errs  []error
+	errs  errors.List
 }
 
 type blockCtx struct {
@@ -275,18 +262,6 @@ type blockCtx struct {
 }
 
 func (bc *blockCtx) findImport(name string) (pr *gox.PkgRef, ok bool) {
-	if pr, ok = bc.imports[name]; ok {
-		return
-	}
-	for k, v := range bc.imports {
-		v.EnsureImported()
-		if v.Types != nil {
-			if v.Types.Name() != k { // if we guess pkgName failed
-				delete(bc.imports, k)
-				bc.imports[v.Types.Name()] = v
-			}
-		}
-	}
 	pr, ok = bc.imports[name]
 	return
 }
@@ -321,10 +296,7 @@ func (p *pkgCtx) loadNamed(at *gox.Package, t *types.Named) {
 }
 
 func (p *pkgCtx) complete() error {
-	if p.errs != nil {
-		return &Errors{Errs: p.errs}
-	}
-	return nil
+	return p.errs.ToError()
 }
 
 func (p *pkgCtx) loadType(name string) {
@@ -401,13 +373,21 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package,
 		DefaultGoFile:   defaultGoFile,
 		NoSkipConstant:  conf.NoSkipConstant,
 	}
+	if enableRecover {
+		defer func() {
+			if e := recover(); e != nil {
+				ctx.handleRecover(e)
+				err = ctx.errs.ToError()
+			}
+		}()
+	}
 	p = gox.NewPackage(pkgPath, pkg.Name, confGox)
 	ctx.cpkgs = cpackages.NewImporter(&cpackages.Config{
 		Pkg: p, LookupPub: conf.LookupPub,
 	})
 	for file, gmx := range files {
 		if gmx.IsProj {
-			ctx.gmxSettings = newGmx(p, file, conf)
+			ctx.gmxSettings = newGmx(ctx, p, file, conf)
 			break
 		}
 	}
@@ -632,7 +612,8 @@ func preloadFile(p *gox.Package, ctx *blockCtx, file string, f *ast.File, genCod
 	parent := ctx.pkgCtx
 	syms := parent.syms
 	goFile := getGoFile(file, genCode)
-	p.SetCurFile(goFile, true)
+	old, _ := p.SetCurFile(goFile, true)
+	defer p.RestoreCurFile(old)
 	for _, decl := range f.Decls {
 		switch d := decl.(type) {
 		case *ast.FuncDecl:
@@ -968,6 +949,13 @@ func simplifyGopPackage(pkgPath string) string {
 }
 
 func loadImport(ctx *blockCtx, spec *ast.ImportSpec) {
+	if enableRecover {
+		defer func() {
+			if e := recover(); e != nil {
+				ctx.handleRecover(e)
+			}
+		}()
+	}
 	var pkg *gox.PkgRef
 	var pkgPath = toString(spec.Path)
 	if realPath, kind := checkC2go(pkgPath); kind != c2goInvalid {
@@ -978,7 +966,7 @@ func loadImport(ctx *blockCtx, spec *ast.ImportSpec) {
 			return
 		}
 	} else {
-		pkg = ctx.pkg.Import(simplifyGopPackage(pkgPath))
+		pkg = ctx.pkg.Import(simplifyGopPackage(pkgPath), spec)
 	}
 	var name string
 	if spec.Name != nil {
@@ -992,7 +980,7 @@ func loadImport(ctx *blockCtx, spec *ast.ImportSpec) {
 			return
 		}
 	} else {
-		name = path.Base(pkgPath) // TODO: open pkgPath to get pkgName
+		name = pkg.Types.Name()
 	}
 	ctx.imports[name] = pkg
 }
@@ -1048,7 +1036,7 @@ func loadVars(ctx *blockCtx, v *ast.ValueSpec, global bool) {
 	if nv := len(v.Values); nv > 0 {
 		cb := varDecl.InitStart(ctx.pkg)
 		if nv == 1 && len(names) == 2 {
-			compileExpr(ctx, v.Values[0], true)
+			compileExpr(ctx, v.Values[0], clCallWithTwoValue)
 		} else {
 			for _, val := range v.Values {
 				switch e := val.(type) {
diff --git a/cl/expr.go b/cl/expr.go
index 80e9fb42..7e3b1bc2 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -55,12 +55,13 @@ Name lookup:
 // ---------------------------------------------------------------------------*/
 
 const (
-	clIdentAutoCall = 1 << iota
+	clIdentCanAutoCall = 1 << iota
 	clIdentAllowBuiltin
 	clIdentLHS
-	clIdentSelectorExpr
+	clIdentSelectorExpr // this ident is X of ast.SelectorExpr
 	clIdentGoto
 	clCallWithTwoValue
+	clCommandWithoutArgs
 )
 
 const (
@@ -159,7 +160,9 @@ func compileMember(ctx *blockCtx, v ast.Node, name string, flags int) error {
 	switch {
 	case (flags & clIdentLHS) != 0:
 		mflag = gox.MemberFlagRef
-	case (flags & clIdentAutoCall) != 0:
+	case (flags & clCommandWithoutArgs) != 0:
+		mflag = gox.MemberFlagMethodAlias
+	case (flags & clIdentCanAutoCall) != 0:
 		mflag = gox.MemberFlagAutoProperty
 	default:
 		mflag = gox.MemberFlagMethodAlias
@@ -183,24 +186,36 @@ func compileExprLHS(ctx *blockCtx, expr ast.Expr) {
 	}
 }
 
-func compileExpr(ctx *blockCtx, expr ast.Expr, twoValue ...bool) {
+func twoValue(inFlags []int) bool {
+	return inFlags != nil && (inFlags[0]&clCallWithTwoValue) != 0
+}
+
+func compileExpr(ctx *blockCtx, expr ast.Expr, inFlags ...int) {
 	switch v := expr.(type) {
 	case *ast.Ident:
-		compileIdent(ctx, v, clIdentAutoCall)
+		flags := clIdentCanAutoCall
+		if inFlags != nil {
+			flags |= inFlags[0]
+		}
+		compileIdent(ctx, v, flags)
 	case *ast.BasicLit:
 		compileBasicLit(ctx, v)
 	case *ast.CallExpr:
 		flags := 0
-		if twoValue != nil && twoValue[0] {
-			flags = clCallWithTwoValue
+		if inFlags != nil {
+			flags = inFlags[0]
 		}
 		compileCallExpr(ctx, v, flags)
 	case *ast.SelectorExpr:
-		compileSelectorExpr(ctx, v, clIdentAutoCall)
+		flags := clIdentCanAutoCall
+		if inFlags != nil {
+			flags |= inFlags[0]
+		}
+		compileSelectorExpr(ctx, v, flags)
 	case *ast.BinaryExpr:
 		compileBinaryExpr(ctx, v)
 	case *ast.UnaryExpr:
-		compileUnaryExpr(ctx, v, twoValue != nil && twoValue[0])
+		compileUnaryExpr(ctx, v, twoValue(inFlags))
 	case *ast.FuncLit:
 		compileFuncLit(ctx, v)
 	case *ast.CompositeLit:
@@ -210,7 +225,7 @@ func compileExpr(ctx *blockCtx, expr ast.Expr, twoValue ...bool) {
 	case *ast.RangeExpr:
 		compileRangeExpr(ctx, v)
 	case *ast.IndexExpr:
-		compileIndexExpr(ctx, v, twoValue != nil && twoValue[0])
+		compileIndexExpr(ctx, v, twoValue(inFlags))
 	case *ast.SliceExpr:
 		compileSliceExpr(ctx, v)
 	case *ast.StarExpr:
@@ -226,13 +241,13 @@ func compileExpr(ctx *blockCtx, expr ast.Expr, twoValue ...bool) {
 	case *ast.InterfaceType:
 		ctx.cb.Typ(toInterfaceType(ctx, v), v)
 	case *ast.ComprehensionExpr:
-		compileComprehensionExpr(ctx, v, twoValue != nil && twoValue[0])
+		compileComprehensionExpr(ctx, v, twoValue(inFlags))
 	case *ast.TypeAssertExpr:
-		compileTypeAssertExpr(ctx, v, twoValue != nil && twoValue[0])
+		compileTypeAssertExpr(ctx, v, twoValue(inFlags))
 	case *ast.ParenExpr:
-		compileExpr(ctx, v.X, twoValue...)
+		compileExpr(ctx, v.X, inFlags...)
 	case *ast.ErrWrapExpr:
-		compileErrWrapExpr(ctx, v)
+		compileErrWrapExpr(ctx, v, 0)
 	case *ast.FuncType:
 		ctx.cb.Typ(toFuncType(ctx, v, nil), v)
 	case *ast.Ellipsis:
@@ -254,7 +269,7 @@ func compileExprOrNone(ctx *blockCtx, expr ast.Expr) {
 
 func compileUnaryExpr(ctx *blockCtx, v *ast.UnaryExpr, twoValue bool) {
 	compileExpr(ctx, v.X)
-	ctx.cb.UnaryOp(gotoken.Token(v.Op), twoValue)
+	ctx.cb.UnaryOp(gotoken.Token(v.Op), twoValue, v)
 }
 
 func compileBinaryExpr(ctx *blockCtx, v *ast.BinaryExpr) {
@@ -320,7 +335,7 @@ func compileSelectorExprLHS(ctx *blockCtx, v *ast.SelectorExpr) {
 func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr, flags int) {
 	switch x := v.X.(type) {
 	case *ast.Ident:
-		if at, kind := compileIdent(ctx, x, flags|clIdentSelectorExpr); kind != objNormal {
+		if at, kind := compileIdent(ctx, x, flags|clIdentCanAutoCall|clIdentSelectorExpr); kind != objNormal {
 			if compilePkgRef(ctx, at, v.Sel, flags, kind) {
 				return
 			}
@@ -385,7 +400,7 @@ func compilePkgRef(ctx *blockCtx, at *gox.PkgRef, x *ast.Ident, flags, pkgKind i
 		if (flags & clIdentLHS) != 0 {
 			cb.VarRef(v, x)
 		} else {
-			autoprop := alias && (flags&clIdentAutoCall) != 0
+			autoprop := alias && (flags&clIdentCanAutoCall) != 0
 			if autoprop && !gox.HasAutoProperty(v.Type()) {
 				return false
 			}
@@ -453,6 +468,16 @@ func compileCallExpr(ctx *blockCtx, v *ast.CallExpr, inFlags int) {
 		compileIdent(ctx, fn, clIdentAllowBuiltin|inFlags)
 	case *ast.SelectorExpr:
 		compileSelectorExpr(ctx, fn, 0)
+	case *ast.ErrWrapExpr:
+		if v.IsCommand() {
+			callExpr := *v
+			callExpr.Fun = fn.X
+			ewExpr := *fn
+			ewExpr.X = &callExpr
+			compileErrWrapExpr(ctx, &ewExpr, inFlags)
+			return
+		}
+		compileErrWrapExpr(ctx, fn, 0)
 	default:
 		compileExpr(ctx, fn)
 	}
@@ -941,14 +966,14 @@ var (
 	tyError = types.Universe.Lookup("error").Type()
 )
 
-func compileErrWrapExpr(ctx *blockCtx, v *ast.ErrWrapExpr) {
+func compileErrWrapExpr(ctx *blockCtx, v *ast.ErrWrapExpr, inFlags int) {
 	pkg, cb := ctx.pkg, ctx.cb
 	useClosure := v.Tok == token.NOT || v.Default != nil
 	if !useClosure && (cb.Scope().Parent() == types.Universe) {
 		panic("TODO: can't use expr? in global")
 	}
 
-	compileExpr(ctx, v.X)
+	compileExpr(ctx, v.X, inFlags)
 	x := cb.InternalStack().Pop()
 	n := 0
 	results, ok := x.Type.(*types.Tuple)
diff --git a/cl/internal/spx2/spx2.go b/cl/internal/spx2/spx2.go
index d4884379..8b83f551 100644
--- a/cl/internal/spx2/spx2.go
+++ b/cl/internal/spx2/spx2.go
@@ -17,8 +17,8 @@
 package spx2
 
 const (
-	Gop_sched  = "Sched"
-	Gop_sprite = "Sprite"
+	Gop_sched = "Sched"
+	Gop_work  = "Sprite"
 )
 
 type Game struct {
diff --git a/cl/stmt.go b/cl/stmt.go
index c788fa99..a4871a34 100644
--- a/cl/stmt.go
+++ b/cl/stmt.go
@@ -81,8 +81,12 @@ func compileStmt(ctx *blockCtx, stmt ast.Stmt) {
 	commentStmt(ctx, stmt)
 	switch v := stmt.(type) {
 	case *ast.ExprStmt:
-		compileExpr(ctx, v.X)
-		if canAutoCall(v.X) && gox.IsFunc(ctx.cb.InternalStack().Get(-1).Type) {
+		inFlags := 0
+		if isCommandWithoutArgs(v.X) {
+			inFlags = clCommandWithoutArgs
+		}
+		compileExpr(ctx, v.X, inFlags)
+		if inFlags != 0 && gox.IsFunc(ctx.cb.InternalStack().Get(-1).Type) {
 			ctx.cb.Call(0)
 		}
 	case *ast.AssignStmt:
@@ -130,7 +134,7 @@ func compileStmt(ctx *blockCtx, stmt ast.Stmt) {
 	ctx.cb.EndStmt()
 }
 
-func canAutoCall(x ast.Expr) bool {
+func isCommandWithoutArgs(x ast.Expr) bool {
 retry:
 	switch t := x.(type) {
 	case *ast.Ident:
@@ -157,11 +161,13 @@ func compileReturnStmt(ctx *blockCtx, expr *ast.ReturnStmt) {
 			}
 			compileCompositeLit(ctx, c, typ, true)
 		} else {
-			twoValue := false
+			inFlags := 0
 			if len(expr.Results) == 1 {
 				if _, ok := ret.(*ast.ComprehensionExpr); ok {
 					results = ctx.cb.Func().Type().(*types.Signature).Results()
-					twoValue = (results.Len() == 2)
+					if results.Len() == 2 {
+						inFlags = clCallWithTwoValue
+					}
 				}
 			}
 			switch v := ret.(type) {
@@ -174,7 +180,7 @@ func compileReturnStmt(ctx *blockCtx, expr *ast.ReturnStmt) {
 				}
 				compileLambda(ctx, v, sig)
 			default:
-				compileExpr(ctx, ret, twoValue)
+				compileExpr(ctx, ret, inFlags)
 			}
 		}
 	}
@@ -194,7 +200,10 @@ func compileSendStmt(ctx *blockCtx, expr *ast.SendStmt) {
 
 func compileAssignStmt(ctx *blockCtx, expr *ast.AssignStmt) {
 	tok := expr.Tok
-	twoValue := (len(expr.Lhs) == 2 && len(expr.Rhs) == 1)
+	inFlags := 0
+	if len(expr.Lhs) == 2 && len(expr.Rhs) == 1 {
+		inFlags = clCallWithTwoValue
+	}
 	if tok == token.DEFINE {
 		names := make([]string, len(expr.Lhs))
 		for i, lhs := range expr.Lhs {
@@ -214,7 +223,7 @@ func compileAssignStmt(ctx *blockCtx, expr *ast.AssignStmt) {
 			}()
 		}
 		for _, rhs := range expr.Rhs {
-			compileExpr(ctx, rhs, twoValue)
+			compileExpr(ctx, rhs, inFlags)
 		}
 		ctx.cb.EndInit(len(expr.Rhs))
 		return
@@ -233,7 +242,7 @@ func compileAssignStmt(ctx *blockCtx, expr *ast.AssignStmt) {
 				panic(ctx.newCodeErrorf(e.Pos(), "lambda unsupport multiple assignment"))
 			}
 		default:
-			compileExpr(ctx, rhs, twoValue)
+			compileExpr(ctx, rhs, inFlags)
 		}
 	}
 	if tok == token.ASSIGN {
@@ -393,6 +402,7 @@ func toForStmt(forPos token.Pos, value ast.Expr, body *ast.BlockStmt, re *ast.Ra
 	}
 	if fp != nil && fp.Cond != nil {
 		condStmt := &ast.IfStmt{
+			If:   fp.IfPos,
 			Init: fp.Init,
 			Cond: fp.Cond,
 			Body: body,
diff --git a/cmd/internal/mod/tidy.go b/cmd/internal/mod/tidy.go
index 615afe46..3157bb47 100644
--- a/cmd/internal/mod/tidy.go
+++ b/cmd/internal/mod/tidy.go
@@ -18,7 +18,6 @@ package mod
 
 import (
 	"fmt"
-	"log"
 	"os"
 	"syscall"
 
@@ -42,8 +41,9 @@ func runTidy(cmd *base.Command, args []string) {
 	if err != nil {
 		if err == syscall.ENOENT {
 			fmt.Fprintln(os.Stderr, "gop.mod not found")
-			os.Exit(1)
+		} else {
+			fmt.Fprintln(os.Stderr, err)
 		}
-		log.Panicln(err)
+		os.Exit(1)
 	}
 }
diff --git a/cmd/make.go b/cmd/make.go
index 3b1cee77..2651b354 100644
--- a/cmd/make.go
+++ b/cmd/make.go
@@ -345,7 +345,7 @@ func buildGoplusTools(useGoProxy bool) {
 		log.Fatalln(err)
 	}
 
-	println("Installing Go+ tools...\n")
+	println("Building Go+ tools...\n")
 	os.Chdir(commandsDir)
 	buildOutput, err := execCommand("go", "build", "-o", gopBinPath, "-v", "-ldflags", buildFlags, "./...")
 	if err != nil {
@@ -356,13 +356,7 @@ func buildGoplusTools(useGoProxy bool) {
 	// Clear gop run cache
 	cleanGopRunCache()
 
-	installPath := linkGoplusToLocalBin()
-
-	println("\nGo+ tools installed successfully!")
-
-	if _, err := execCommand("gop", "version"); err != nil {
-		showHelpPostInstall(installPath)
-	}
+	println("\nGo+ tools built successfully!")
 }
 
 func showHelpPostInstall(installPath string) {
@@ -375,6 +369,17 @@ we recommend you add the above install directory into your PATH environment vari
 	println(message)
 }
 
+// Install Go+ tools
+func install() {
+	installPath := linkGoplusToLocalBin()
+
+	println("\nGo+ tools installed successfully!")
+
+	if _, err := execCommand("gop", "version"); err != nil {
+		showHelpPostInstall(installPath)
+	}
+}
+
 func runTestcases() {
 	println("Start running testcases.")
 	os.Chdir(gopRoot)
@@ -525,6 +530,7 @@ func releaseNewVersion(tag string) {
 
 func main() {
 	isInstall := flag.Bool("install", false, "Install Go+")
+	isBuild := flag.Bool("build", false, "Build Go+ tools")
 	isTest := flag.Bool("test", false, "Run testcases")
 	isUninstall := flag.Bool("uninstall", false, "Uninstall Go+")
 	isGoProxy := flag.Bool("proxy", false, "Set GOPROXY for people in China")
@@ -539,13 +545,17 @@ func main() {
 		useGoProxy = isInChina()
 	}
 	flagActionMap := map[*bool]func(){
-		isInstall:   func() { buildGoplusTools(useGoProxy) },
+		isBuild: func() { buildGoplusTools(useGoProxy) },
+		isInstall: func() {
+			buildGoplusTools(useGoProxy)
+			install()
+		},
 		isUninstall: uninstall,
 		isTest:      runTestcases,
 	}
 
 	// Sort flags, for example: install flag should be checked earlier than test flag.
-	flags := []*bool{isInstall, isTest, isUninstall}
+	flags := []*bool{isBuild, isInstall, isTest, isUninstall}
 	hasActionDone := false
 
 	if *tag != "" {
diff --git a/doc/contributing.md b/doc/contributing.md
new file mode 100644
index 00000000..0ff69387
--- /dev/null
+++ b/doc/contributing.md
@@ -0,0 +1,29 @@
+## Code Structure
+
+TODO
+
+## Contributing
+
+The Go+ project welcomes all contributors. We appreciate your help!
+
+Here are [list of Go+ Contributors](https://github.com/goplus/gop/wiki/Goplus-Contributors). We award an email account (XXX@goplus.org) for every contributor. And we suggest you commit code by using this email account:
+
+```bash
+git config --global user.email XXX@goplus.org
+```
+
+If you did this, remember to add your `XXX@goplus.org` email to https://github.com/settings/emails.
+
+What does `a contributor to Go+` mean? You must meet one of the following conditions:
+
+* At least one pull request of a full-featured implemention.
+* At least three pull requests of feature enhancements.
+* At least ten pull requests of any kind issues.
+
+Where can you start?
+
+* [![Issues](https://img.shields.io/badge/ISSUEs-Go+-blue.svg)](https://github.com/goplus/gop/issues)
+* [![Issues](https://img.shields.io/badge/ISSUEs-NumGo+-blue.svg)](https://github.com/numgoplus/ng/issues)
+* [![Issues](https://img.shields.io/badge/ISSUEs-PandasGo+-blue.svg)](https://github.com/goplus/pandas/issues)
+* [![Issues](https://img.shields.io/badge/ISSUEs-vscode%20Go+-blue.svg)](https://github.com/gopcode/vscode-goplus/issues)
+* [![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/goplus/gop)](https://www.tickgit.com/browse?repo=github.com/goplus/gop)
diff --git a/doc/docs.md b/doc/docs.md
new file mode 100644
index 00000000..d45741bc
--- /dev/null
+++ b/doc/docs.md
@@ -0,0 +1,1259 @@
+Go+ Quick Start
+======
+
+The Go+ programming language is designed for engineering, STEM education, and data science.
+
+* **For engineering**: working in the simplest language that can be mastered by children.
+* **For STEM education**: studying an engineering language that can be used for work in the future.
+* **For data science**: communicating with engineers in the same language.
+
+## How to install
+
+For now, we suggest you install Go+ from source code.
+
+Note: Requires go1.16 or later
+
+```bash
+git clone https://github.com/goplus/gop.git
+cd gop
+
+# On mac/linux run:
+./all.bash
+# On Windows run:
+all.bat
+```
+
+Actually, `all.bash` and `all.bat` will use `go run cmd/make.go` underneath.
+
+
+## Running in Go+ playground
+
+If you don't want install Go+, you can write your Go+ programs in Go+ playground. This is the fastest way to experience Go+.
+
+* Go+ playground based on Docker: https://play.goplus.org/
+* Go+ playground based on GopherJS: https://jsplay.goplus.org/
+
+And you can share your Go+ code with your friends.
+Here is my `Hello world` program:
+* https://play.goplus.org/p/AAh_gQAKAZR.
+
+
+## Table of Contents
+
+<table>
+    <tr><td width=33% valign=top>
+
+* [Hello world](#hello-world)
+* [Running a project folder](#running-a-project-folder-with-several-files)
+* [Comments](#comments)
+* [Variables](#variables)
+* [Go+ types](#go-types)
+    * [Strings](#strings)
+    * [Numbers](#numbers)
+    * [Slices](#slices)
+    * [Maps](#maps)
+* [Module imports](#module-imports)
+
+</td><td width=33% valign=top>
+
+* [Statements & expressions](#statements--expressions)
+    * [If..else](#ifelse)
+    * [For loop](#for-loop)
+    * [Error handling](#error-handling)
+* [Functions](#functions)
+    * [Returning multiple values](#returning-multiple-values)
+    * [Variadic parameters](#variadic-parameters)
+    * [Higher order functions](#higher-order-functions)
+    * [Lambda expressions](#lambda-expressions)
+* [Structs](#structs)
+
+</td><td valign=top>
+
+* [Go/Go+ hybrid programming](#gogo-hybrid-programming)
+* [Calling C from Go+](#calling-c-from-go)
+* [Data processing](#data-processing)
+    * [Rational numbers](#rational-numbers)
+    * [List comprehension](#list-comprehension)
+    * [Select data from a collection](#select-data-from-a-collection)
+    * [Check if data exists in a collection](#check-if-data-exists-in-a-collection)
+* [Unix shebang](#unix-shebang)
+* [Compatibility with Go](#compatibility-with-go)
+
+</td></tr>
+</table>
+
+
+## Hello World
+
+```go
+println "Hello world"
+```
+
+Save this snippet into a file named `hello.gop`. Now do: `gop run hello.gop`.
+
+Congratulations - you just wrote and executed your first Go+ program!
+
+You can compile a program without execution with `gop build hello.gop`.
+See `gop help` for all supported commands.
+
+[`println`](#println) is one of the few [built-in functions](#builtin-functions).
+It prints the value passed to it to standard output.
+
+See https://tutorial.goplus.org/hello-world for more details.
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+## Running a project folder with several files
+
+Suppose you have a folder with several .gop files in it, and you want 
+to compile them all into one program. Just do: `gop run .`.
+
+Passing parameters also works, so you can do:
+`gop run . --yourparams some_other_stuff`.
+
+Your program can then use the CLI parameters like this:
+
+```go
+import "os"
+
+println os.Args
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+## Comments
+
+```go
+# This is a single line comment.
+
+// This is a single line comment.
+
+/*
+This is a multiline comment.
+*/
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+## Variables
+
+```go
+name := "Bob"
+age := 20
+largeNumber := int128(1 << 65)
+println name, age
+println largeNumber
+```
+
+Variables are declared and initialized with `:=`.
+
+The variable's type is inferred from the value on the right hand side.
+To choose a different type, use type conversion:
+the expression `T(v)` converts the value `v` to the
+type `T`.
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+### Initialization vs. assignment
+
+Note the (important) difference between `:=` and `=`.
+`:=` is used for declaring and initializing, `=` is used for assigning.
+
+```go failcompile
+age = 21
+```
+
+This code will not compile, because the variable `age` is not declared.
+All variables need to be declared in Go+.
+
+```go
+age := 21
+```
+
+The values of multiple variables can be changed in one line.
+In this way, their values can be swapped without an intermediary variable.
+
+```go
+a, b := 0, 1
+a, b = b, a
+println a, b // 1, 0
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+## Go+ Types
+
+### Primitive types
+
+```go ignore
+bool
+
+int8    int16   int32   int    int64    int128
+uint8   uint16  uint32  uint   uint64   uint128
+
+uintptr // similar to C's size_t
+
+byte // alias for uint8
+rune // alias for int32, represents a Unicode code point
+
+string
+
+float32 float64
+
+complex64 complex128
+
+bigint bigrat
+
+unsafe.Pointer // similar to C's void*
+
+any // alias for Go's interface{}
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+### Strings
+
+```go
+name := "Bob"
+println name.len  // 3
+println name[0]   // 66
+println name[1:3] // ob
+println name[:2]  // Bo
+println name[2:]  // b
+
+// or using octal escape `\###` notation where `#` is an octal digit
+println "\141a"   // aa
+
+// Unicode can be specified directly as `\u####` where # is a hex digit
+// and will be converted internally to its UTF-8 representation
+println "\u2605"  // ★
+```
+
+String values are immutable. You cannot mutate elements:
+
+```go failcompile
+s := "hello 🌎"
+s[0] = `H` // not allowed
+```
+
+Note that indexing a string will produce a `byte`, not a `rune` nor another `string`.
+
+Strings can be easily converted to integers:
+
+```go
+s := "12"
+a, err := s.int
+b := s.int! // will panic if s isn't a valid integer
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+#### String operators
+
+```go
+name := "Bob"
+bobby := name + "by" // + is used to concatenate strings
+println bobby // Bobby
+
+s := "Hello "
+s += "world"
+println s // Hello world
+```
+
+All operators in Go+ must have values of the same type on both sides. You cannot concatenate an
+integer to a string:
+
+```go failcompile
+age := 10
+println "age = " + age // not allowed
+```
+
+We have to either convert `age` to a `string`:
+
+```go
+age := 10
+println "age = " + age.string
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### Runes
+
+A `rune` represents a single Unicode character and is an alias for `int32`.
+
+```go
+rocket := '🚀'
+println rocket         // 128640
+println string(rocket) // 🚀
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### Numbers
+
+```go
+a := 123
+```
+
+This will assign the value of 123 to `a`. By default `a` will have the
+type `int`.
+
+You can also use hexadecimal, binary or octal notation for integer literals:
+
+```go
+a := 0x7B
+b := 0b01111011
+c := 0o173
+```
+
+All of these will be assigned the same value, 123. They will all have type
+`int`, no matter what notation you used.
+
+Go+ also supports writing numbers with `_` as separator:
+
+```go
+num := 1_000_000 // same as 1000000
+```
+
+If you want a different type of integer, you can use casting:
+
+```go
+a := int64(123)
+b := uint8(12)
+c := int128(12345)
+```
+
+Assigning floating point numbers works the same way:
+
+```go
+f1 := 1.0
+f2 := float32(3.14)
+```
+
+If you do not specify the type explicitly, by default float literals will have the type of `float64`.
+
+Float literals can also be declared as a power of ten:
+
+```go
+f0 := 42e1   // 420
+f1 := 123e-2 // 1.23
+f2 := 456e+2 // 45600
+```
+
+Go+ has built-in support for [rational numbers](#rational-numbers):
+
+```go
+a := 1r << 200  // suffix `r` means `rational`
+b := bigint(1 << 200)
+```
+
+And you can cast bool to number types (this is NOT supported in Go):
+
+```go
+println int(true)       // 1
+println float64(true)   // 1
+println complex64(true) // (1+0i)
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### Slices
+
+A slice is a collection of data elements of the same type. A slice literal is a
+list of expressions surrounded by square brackets. An individual element can be
+accessed using an *index* expression. Indexes start from `0`:
+
+```go
+nums := [1, 2, 3]
+println nums      // [1 2 3]
+println nums.len  // 3
+println nums[0]   // 1
+println nums[1:3] // [2 3]
+println nums[:2]  // [1 2]
+println nums[2:]  // [3]
+
+nums[1] = 5
+println nums // [1 5 3]
+```
+
+Type of a slice literal is infered automatically.
+
+```go
+a := [1, 2, 3]   // []int
+b := [1, 2, 3.4] // []float64
+c := ["Hi"]      // []string
+d := ["Hi", 10]  // []any
+d := []          // []any
+```
+
+And casting slice literals also works.
+
+```go
+a := []float64([1, 2, 3]) // []float64
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### Maps
+
+```go
+a := {"Hello": 1, "xsw": 3}     // map[string]int
+b := {"Hello": 1, "xsw": 3.4}   // map[string]float64
+c := {"Hello": 1, "xsw": "Go+"} // map[string]any
+d := {}                         // map[string]any
+```
+
+If a key is not found, a zero value is returned by default:
+
+```go
+a := {"Hello": 1, "xsw": 3}
+c := {"Hello": 1, "xsw": "Go+"}
+println a["bad_key"] // 0
+println c["bad_key"] // <nil>
+```
+
+You can also check, if a key is present, and get its value.
+
+```go
+a := {"Hello": 1, "xsw": 3}
+if v, ok := a["xsw"]; ok {
+    println "its value is", v
+}
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+## Module imports
+
+For information about creating a module, see [Modules](#modules).
+
+Modules can be imported using the `import` keyword:
+
+```go
+import "strings"
+
+x := strings.NewReplacer("?", "!").Replace("Hello, world???")
+println x // Hello, world!!!
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### Module import aliasing
+
+Any imported module name can be aliased:
+
+```go
+import strop "strings"
+
+x := strop.NewReplacer("?", "!").Replace("Hello, world???")
+println x // Hello, world!!!
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+## Statements & expressions
+
+
+### If..else
+
+In Go+, `if` statements are pretty straightforward and similar to most other languages.
+Unlike other C-like languages,
+there are no parentheses surrounding the condition and the braces are always required.
+
+```go
+a := 10
+b := 20
+if a < b {
+    println "a < b"
+} else if a > b {
+    println "a > b"
+} else {
+    println "a == b"
+}
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### For loop
+
+Go+ has only one looping keyword: `for`, with several forms.
+
+#### `for`/`<-`
+
+This is the most common form. You can use it with a slice, map, numeric range or custom iterators.
+
+For information about creating a custom iterators, see [Custom iterators](#custom-iterators).
+
+##### Slice `for`
+
+The `for value <- arr` form is used for going through elements of a slice.
+
+```go
+numbers := [1, 3, 5, 7, 11, 13, 17]
+sum := 0
+for x <- numbers {
+    sum += x
+}
+println sum // 57
+```
+
+If an index is required, an alternative form `for index, value <- arr` can be used.
+
+```go
+names := ["Sam", "Peter"]
+for i, name <- names {
+    println i, name
+    // 0 Sam
+    // 1 Peter
+}
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+##### Map `for`
+
+```go
+m := {"one": 1, "two": 2}
+for key, val <- m {
+    println key, val
+    // one 1
+    // two 2
+}
+for key, _ <- m {
+    println key
+    // one
+    // two
+}
+for val <- m {
+    println val
+    // 1
+    // 2
+}
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+##### Range `for`
+
+You can use `range expression` (`start:end:step`) in for loop.
+
+```go
+for i <- :5 {
+    println i
+    // 0
+    // 1
+    // 2
+    // 3
+    // 4
+}
+for i <- 1:5 {
+    println i
+    // 1
+    // 2
+    // 3
+    // 4
+}
+for i <- 1:5:2 {
+    println i
+    // 1
+    // 3
+}
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+##### `for`/`<-`/`if`
+
+All loops of `for`/`<-` form can have an optional `if` condition.
+
+```go
+numbers := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+for num <- numbers if num%3 == 0 {
+    println num
+    // 0
+    // 3
+    // 6
+    // 9
+}
+
+for num <- :10 if num%3 == 0 {
+    println num
+    // 0
+    // 3
+    // 6
+    // 9
+}
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+#### Condition `for`
+
+```go
+sum := 0
+i := 1
+for i <= 100 {
+    sum += i
+    i++
+}
+println sum // 5050
+```
+
+This form of the loop is similar to `while` loops in other languages.
+The loop will stop iterating once the boolean condition evaluates to false.
+Again, there are no parentheses surrounding the condition, and the braces are always required.
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+#### C `for`
+
+```go
+for i := 0; i < 10; i += 2 {
+    // Don't print 6
+    if i == 6 {
+        continue
+    }
+    println i
+    // 0
+    // 2
+    // 4
+    // 8
+}
+```
+
+Finally, there's the traditional C style `for` loop. It's safer than the `while` form
+because with the latter it's easy to forget to update the counter and get
+stuck in an infinite loop.
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+#### Bare `for`
+
+```go
+for {
+    // ...
+}
+```
+
+The condition can be omitted, resulting in an infinite loop. You can use `break` or `return` to end the loop.
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### Error handling
+
+We reinvent the error handling specification in Go+. We call them `ErrWrap expressions`:
+
+```go
+expr! // panic if err
+expr? // return if err
+expr?:defval // use defval if err
+```
+
+How to use them? Here is an example:
+
+```go
+import (
+    "strconv"
+)
+
+func add(x, y string) (int, error) {
+    return strconv.Atoi(x)? + strconv.Atoi(y)?, nil
+}
+
+func addSafe(x, y string) int {
+    return strconv.Atoi(x)?:0 + strconv.Atoi(y)?:0
+}
+
+println `add("100", "23"):`, add("100", "23")!
+
+sum, err := add("10", "abc")
+println `add("10", "abc"):`, sum, err
+
+println `addSafe("10", "abc"):`, addSafe("10", "abc")
+```
+
+The output of this example is:
+
+```
+add("100", "23"): 123
+add("10", "abc"): 0 strconv.Atoi: parsing "abc": invalid syntax
+
+===> errors stack:
+main.add("10", "abc")
+    /Users/xsw/tutorial/15-ErrWrap/err_wrap.gop:6 strconv.Atoi(y)?
+
+addSafe("10", "abc"): 10
+```
+
+Compared to corresponding Go code, It is clear and more readable.
+
+And the most interesting thing is, the return error contains the full error stack. When we got an error, it is very easy to position what the root cause is.
+
+How these `ErrWrap expressions` work? See [Error Handling](https://github.com/goplus/gop/wiki/Error-Handling) for more information.
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+## Functions
+
+```go
+func add(x int, y int) int {
+    return x + y
+}
+
+println add(2, 3) // 5
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### Returning multiple values
+
+```go
+func foo() (int, int) {
+    return 2, 3
+}
+
+a, b := foo()
+println a // 2
+println b // 3
+c, _ := foo() // ignore values using `_`
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### Variadic parameters
+
+```go
+func sum(a ...int) int {
+    total := 0
+    for x <- a {
+        total += x
+    }
+    return total
+}
+
+println sum(2, 3, 5) // 10
+```
+
+Output parameters can have names.
+
+```go
+func sum(a ...int) (total int) {
+    for x <- a {
+        total += x
+    }
+    return // don't need return values if they are assigned
+}
+
+println sum(2, 3, 5) // 10
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### Higher order functions
+
+Functions can also be parameters.
+
+```go
+func square(x float64) float64 {
+    return x*x
+}
+
+func abs(x float64) float64 {
+    if x < 0 {
+        return -x
+    }
+    return x
+}
+
+func transform(a []float64, f func(float64) float64) []float64 {
+    return [f(x) for x <- a]
+}
+
+y := transform([1, 2, 3], square)
+println y // [1 4 9]
+
+z := transform([-3, 1, -5], abs)
+println z // [3 1 5]
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### Lambda expressions
+
+You also can use `lambda expression` to define a anonymous function.
+
+```go
+func transform(a []float64, f func(float64) float64) []float64 {
+    return [f(x) for x <- a]
+}
+
+y := transform([1, 2, 3], x => x*x)
+println y // [1 4 9]
+
+z := transform([-3, 1, -5], x => {
+    if x < 0 {
+        return -x
+    }
+    return x
+})
+println z // [3 1 5]
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+## Structs
+
+### Custom iterators
+
+#### For range of UDT
+
+```go
+type Foo struct {
+}
+
+// Gop_Enum(proc func(val ValType)) or:
+// Gop_Enum(proc func(key KeyType, val ValType))
+func (p *Foo) Gop_Enum(proc func(key int, val string)) {
+    // ...
+}
+
+foo := &Foo{}
+for k, v := range foo {
+    println k, v
+}
+
+for k, v <- foo {
+    println k, v
+}
+
+println {v: k for k, v <- foo}
+```
+
+**Note: you can't use break/continue or return statements in for range of udt.Gop_Enum(callback).**
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+#### For range of UDT2
+
+```go
+type FooIter struct {
+}
+
+// (Iterator) Next() (val ValType, ok bool) or:
+// (Iterator) Next() (key KeyType, val ValType, ok bool)
+func (p *FooIter) Next() (key int, val string, ok bool) {
+    // ...
+}
+
+type Foo struct {
+}
+
+// Gop_Enum() Iterator
+func (p *Foo) Gop_Enum() *FooIter {
+    // ...
+}
+
+foo := &Foo{}
+for k, v := range foo {
+    println k, v
+}
+
+for k, v <- foo {
+    println k, v
+}
+
+println {v: k for k, v <- foo}
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### Deduce struct type
+
+```go
+type Config struct {
+    Dir   string
+    Level int
+}
+
+func foo(conf *Config) {
+    // ...
+}
+
+foo {Dir: "/foo/bar", Level: 1}
+```
+
+Here `foo {Dir: "/foo/bar", Level: 1}` is equivalent to `foo(&Config{Dir: "/foo/bar", Level: 1})`. However, you can't replace `foo(&Config{"/foo/bar", 1})` with `foo {"/foo/bar", 1}`, because it is confusing to consider `{"/foo/bar", 1}` as a struct literal.
+
+You also can omit struct types in a return statement. For example:
+
+```go
+type Result struct {
+    Text string
+}
+
+func foo() *Result {
+    return {Text: "Hi, Go+"} // return &Result{Text: "Hi, Go+"}
+}
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### Overload operators
+
+```go
+import "math/big"
+
+type MyBigInt struct {
+    *big.Int
+}
+
+func Int(v *big.Int) MyBigInt {
+    return MyBigInt{v}
+}
+
+func (a MyBigInt) + (b MyBigInt) MyBigInt { // binary operator
+    return MyBigInt{new(big.Int).Add(a.Int, b.Int)}
+}
+
+func (a MyBigInt) += (b MyBigInt) {
+    a.Int.Add(a.Int, b.Int)
+}
+
+func -(a MyBigInt) MyBigInt { // unary operator
+    return MyBigInt{new(big.Int).Neg(a.Int)}
+}
+
+a := Int(1r)
+a += Int(2r)
+println a + Int(3r)
+println -a
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### Auto property
+
+Let's see an example written in Go+:
+
+```go
+import "gop/ast/goptest"
+
+doc := goptest.New(`... Go+ code ...`)!
+
+println doc.Any().FuncDecl().Name()
+```
+
+In many languages, there is a concept named `property` who has `get` and `set` methods.
+
+Suppose we have `get property`, the above example will be:
+
+```go
+import "gop/ast/goptest"
+
+doc := goptest.New(`... Go+ code ...`)!
+
+println doc.any.funcDecl.name
+```
+
+In Go+, we introduce a concept named `auto property`. It is a `get property`, but is implemented automatically. If we have a method named `Bar()`, then we will have a `get property` named `bar` at the same time.
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+## Go/Go+ hybrid programming
+
+This is an example to show how to mix Go/Go+ code in the same package.
+
+In this example, we have a Go source file named `a.go`:
+
+```go
+package main
+
+import "fmt"
+
+func p(a interface{}) {
+    sayMix()
+    fmt.Println("Hello,", a)
+}
+```
+
+And we have a Go+ source file named `b.gop`:
+
+```go
+func sayMix() {
+    println "Mix Go and Go+"
+}
+
+p "world"
+```
+
+You can see that Go calls a Go+ function named `sayMix`, and Go+ calls a Go function named `p`. As you are used to in Go programming, this kind of circular reference is allowed.
+
+Run `gop run .` to see the output of this example:
+
+```
+Mix Go and Go+
+Hello, world
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+## Calling C from Go+
+
+- The `gop c` command (equivalent to the stand-alone `c2go` command) can be used to convert a C project to a Go project.
+- `import "C"` and `import "C/xxx"` are used to import a C project converted by c2go. where `import "C"` is short for `import "C/github.com/goplus/libc"`.
+- The `C"xxx"` syntax represents C-style string constants.
+
+Here is [an example to show how Go+ interacts with C](https://github.com/goplus/gop/tree/v1.1/testdata/helloc2go).
+
+```go
+import "C"
+
+C.printf C"Hello, c2go!\n"
+C.fprintf C.stderr, C"Hi, %7.1f\n", 3.14
+```
+
+In this example we call two C standard functions `printf` and `fprintf`, passing a C variable `stderr` and two C strings in the form of `C"xxx"` (a Go+ syntax to represent C-style strings).
+
+Run `gop run .` to see the output of this example:
+
+```
+Hello, c2go!
+Hi,     3.1
+```
+
+Of course, the current Go+ support for C is only a preview version, not to the extent that it is actually available in engineering. As far as libc is concerned, the current migration progress is only about 5%, and it is just the beginning.
+
+In the upcoming Go+ v1.2 version planning, complete support for C is listed as a top priority. Of course, support for cgo and Go templates is also under planning, which is a crucial capability enhancement for Go/Go+ hybrid projects.
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+## Data processing
+
+### Rational numbers
+
+We introduce rational numbers as primitive Go+ types. We use suffix `r` to denote rational literals. For example, `1r << 200` means a big int whose value is equal to 2<sup>200</sup>.
+
+```go
+a := 1r << 200
+b := bigint(1 << 200)
+```
+
+By default, `1r` will have the type of `bigint`.
+
+And `4/5r` means the rational constant `4/5`.
+It will have the type of `bigrat`.
+
+```go
+a := 4/5r
+b := a - 1/3r + 3 * 1/2r
+println a, b // 4/5 59/30
+```
+
+Casting rational numbers works like other [primitive types](#primitive-types)):
+
+```go
+a := 1r
+b := bigrat(1r)
+c := bigrat(1)
+println a/3 // 0
+println b/3 // 1/3
+println c/3 // 1/3
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### List comprehension
+
+```go
+a := [x*x for x <- [1, 3, 5, 7, 11]]
+b := [x*x for x <- [1, 3, 5, 7, 11] if x > 3]
+c := [i+v for i, v <- [1, 3, 5, 7, 11] if i%2 == 1]
+
+arr := [1, 2, 3, 4, 5, 6]
+d := [[a, b] for a <- arr if a < b for b <- arr if b > 2]
+
+x := {x: i for i, x <- [1, 3, 5, 7, 11]}
+y := {x: i for i, x <- [1, 3, 5, 7, 11] if i%2 == 1}
+z := {v: k for k, v <- {1: "Hello", 3: "Hi", 5: "xsw", 7: "Go+"} if k > 3}
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### Select data from a collection
+
+```go
+type student struct {
+    name  string
+    score int
+}
+
+students := [student{"Ken", 90}, student{"Jason", 80}, student{"Lily", 85}]
+
+unknownScore, ok := {x.score for x <- students if x.name == "Unknown"}
+jasonScore := {x.score for x <- students if x.name == "Jason"}
+
+println unknownScore, ok // 0 false
+println jasonScore // 80
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+### Check if data exists in a collection
+
+```go
+type student struct {
+    name  string
+    score int
+}
+
+students := [student{"Ken", 90}, student{"Jason", 80}, student{"Lily", 85}]
+
+hasJason := {for x <- students if x.name == "Jason"} // is any student named Jason?
+hasFailed := {for x <- students if x.score < 60}     // is any student failed?
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+## Unix shebang
+
+You can use Go+ programs as shell scripts now. For example:
+
+```go
+#!/usr/bin/env -S gop run
+
+println "Hello, Go+"
+
+println 1r << 129
+println 1/3r + 2/7r*2
+
+arr := [1, 3, 5, 7, 11, 13, 17, 19]
+println arr
+println [x*x for x <- arr, x > 3]
+
+m := {"Hi": 1, "Go+": 2}
+println m
+println {v: k for k, v <- m}
+println [k for k, _ <- m]
+println [v for v <- m]
+```
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+## Compatibility with Go
+
+All Go features will be supported (including partially support `cgo`, see [below](#bytecode-vs-go-code)).
+
+**All Go packages (even these packages use `cgo`) can be imported by Go+.**
+
+```coffee
+import (
+    "fmt"
+    "strings"
+)
+
+x := strings.NewReplacer("?", "!").Replace("hello, world???")
+fmt.Println "x:", x
+```
+
+**And all Go+ packages can also be imported in Go programs. What you need to do is just using `gop` command instead of `go`.**
+
+First, let's make a directory named `14-Using-goplus-in-Go`.
+
+Then write a Go+ package named [foo](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go/foo) in it:
+
+```go
+package foo
+
+func ReverseMap(m map[string]int) map[int]string {
+    return {v: k for k, v <- m}
+}
+```
+
+Then use it in a Go package [14-Using-goplus-in-Go/gomain](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go/gomain):
+
+```go
+package main
+
+import (
+    "fmt"
+
+    "github.com/goplus/tutorial/14-Using-goplus-in-Go/foo"
+)
+
+func main() {
+    rmap := foo.ReverseMap(map[string]int{"Hi": 1, "Hello": 2})
+    fmt.Println(rmap)
+}
+```
+
+How to build this example? You can use:
+
+```bash
+gop install -v ./...
+```
+
+Go [github.com/goplus/tutorial/14-Using-goplus-in-Go](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go) to get the source code.
+
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+## Bytecode vs. Go code
+
+Go+ supports bytecode backend and Go code generation.
+
+When we use `gop` command, it generates Go code to covert Go+ package into Go packages.
+
+```bash
+gop run     # Run a Go+ program
+gop install # Build Go+ files and install target to GOBIN
+gop build   # Build Go+ files
+gop test    # Test Go+ packages
+gop fmt     # Format Go+ packages
+gop clean   # Clean all Go+ auto generated files
+gop go      # Convert Go+ packages into Go packages
+```
+
+When we use [`igop`](https://github.com/goplus/igop) command, it generates bytecode to execute.
+
+```bash
+igop  # Run a Go+ program
+```
+
+In bytecode mode, Go+ doesn't support `cgo`. However, in Go-code-generation mode, Go+ fully supports `cgo`.
diff --git a/doc/goplus-vs-go.md b/doc/goplus-vs-go.md
new file mode 100644
index 00000000..6aca7d2f
--- /dev/null
+++ b/doc/goplus-vs-go.md
@@ -0,0 +1,16 @@
+Go+ vs. Go
+======
+
+What's the difference between Go+ and Go?
+
+The goal of Go is to provide a way to easily build simple, reliable, and efficient software. It wants to be a better C.
+
+The goal of Go+ is to achieve a natural fusion of engineering and low-code. It wants to be a better Python.
+
+The essence of low-code is non-professionals oriented. Today's mainstream programming language designers still take non-professionals as a niche group. But Go+ take this group as vital, and they are a vital breakthrough in today's programming language revolution.
+
+The Python language appeared very early (1991), but its design idea is very far-sighted, and it is a rare non-professionals oriented programming language so far. There are quite a few languages that look relatively concise, such as Ruby and CoffeeScript, but they have many language magics so that people feel they are flexible and powerful, but not easy to master. They cannot be regarded as non-professionals oriented.
+
+Python is very popular today, but it's hard to call it an engineering language. Most people just take it as a tool for rapid prototyping.
+
+Is it possible to minimize the complexity of engineering and present it in a low-code form? Go+ hopes to explore a new path at this point.
diff --git a/gendeps.go b/gendeps.go
index b4f1deb5..be9bf9e2 100644
--- a/gendeps.go
+++ b/gendeps.go
@@ -36,6 +36,9 @@ import (
 func GenDepMods(mod *gopmod.Module, dir string, recursively bool) (ret map[string]struct{}, err error) {
 	modBase := mod.Path()
 	ret = make(map[string]struct{})
+	for _, r := range mod.Register {
+		ret[r.ClassfileMod] = struct{}{}
+	}
 	err = HandleDeps(mod, dir, recursively, func(pkgPath string) {
 		modPath, _ := modfetch.Split(pkgPath, modBase)
 		if modPath != "" && modPath != modBase {
diff --git a/gengo.go b/gengo.go
index badd25c3..fb7d943e 100644
--- a/gengo.go
+++ b/gengo.go
@@ -17,7 +17,6 @@
 package gop
 
 import (
-	"fmt"
 	"io/fs"
 	"os"
 	"path/filepath"
@@ -27,6 +26,7 @@ import (
 	"github.com/goplus/mod/gopmod"
 	"github.com/goplus/mod/modcache"
 	"github.com/goplus/mod/modfetch"
+	"github.com/qiniu/x/errors"
 )
 
 const (
@@ -48,24 +48,29 @@ func GenGo(dir string, conf *Config, genTestPkg bool) (string, bool, error) {
 
 func genGoDir(dir string, conf *Config, genTestPkg, recursively bool) (err error) {
 	if recursively {
-		return filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
+		var list errors.List
+		fn := func(path string, d fs.DirEntry, err error) error {
 			if err == nil && d.IsDir() {
 				if strings.HasPrefix(d.Name(), "_") { // skip _
 					return filepath.SkipDir
 				}
-				err = genGoIn(path, conf, genTestPkg, true)
-				if err != nil {
-					fmt.Fprintln(os.Stderr, err)
+				if e := genGoIn(path, conf, genTestPkg, true); e != nil {
+					list.Add(e)
 				}
 			}
 			return err
-		})
+		}
+		err = filepath.WalkDir(dir, fn)
+		if err != nil {
+			return errors.NewWith(err, `filepath.WalkDir(dir, fn)`, -2, "filepath.WalkDir", dir, fn)
+		}
+		return list.ToError()
 	}
 	return genGoIn(dir, conf, genTestPkg, false)
 }
 
 func genGoIn(dir string, conf *Config, genTestPkg, prompt bool) (err error) {
-	out, test, err := LoadDir(dir, conf, genTestPkg)
+	out, test, err := LoadDir(dir, conf, genTestPkg, prompt)
 	if err != nil {
 		if err == syscall.ENOENT { // no Go+ source files
 			return nil
@@ -73,10 +78,6 @@ func genGoIn(dir string, conf *Config, genTestPkg, prompt bool) (err error) {
 		return
 	}
 
-	if prompt {
-		fmt.Printf("GenGo %v ...\n", dir)
-	}
-
 	os.MkdirAll(dir, 0755)
 	file := filepath.Join(dir, autoGenFile)
 	err = out.WriteFile(file)
diff --git a/go.mod b/go.mod
index ca62f731..2fc4a25a 100644
--- a/go.mod
+++ b/go.mod
@@ -3,9 +3,9 @@ module github.com/goplus/gop
 go 1.16
 
 require (
-	github.com/goplus/c2go v0.7.6
-	github.com/goplus/gox v1.11.8
-	github.com/goplus/libc v0.3.8
-	github.com/goplus/mod v0.9.10
-	github.com/qiniu/x v1.11.5
+	github.com/goplus/c2go v0.7.8
+	github.com/goplus/gox v1.11.12
+	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 3fc1ffcb..2cf1ca6b 100644
--- a/go.sum
+++ b/go.sum
@@ -2,14 +2,14 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/goplus/c2go v0.7.6 h1:8+kCXKqrhXRkRxH0v9FjjAAUYL3V27LVxvjDGhYJxgg=
-github.com/goplus/c2go v0.7.6/go.mod h1:NedKEtme4+2tPEK3IEtACPvUPm/Bq9+myFVcbxQK8yg=
-github.com/goplus/gox v1.11.8 h1:XVyhun8eq2gIAPpIKq+5MFoFqe+epALYUo00c66qH8g=
-github.com/goplus/gox v1.11.8/go.mod h1:gu7fuQF8RmWPZUjd+tEJGuV3m/vOEv0bHrct0x/KatM=
-github.com/goplus/libc v0.3.8 h1:0JlN9hJ5x5wXgE1q0DOKLQvFW/42PwIEqlKwVfhkZLw=
-github.com/goplus/libc v0.3.8/go.mod h1:5jwI9WTo0535XwnR5dFZuKgIXm2b2dOqulFon7C1yIE=
-github.com/goplus/mod v0.9.10 h1:z7wrTT241dPSGJDeMs5uuqv/5CgXLomhOZGWEAZ+OpE=
-github.com/goplus/mod v0.9.10/go.mod h1:NHU13OjeNV3ez1f+R9FLJIlIun0KNSuZb0jnmP3N3o8=
+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/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=
+github.com/goplus/mod v0.9.12/go.mod h1:YoPIowz71rnLLROA4YG0AC8bzDtPRyMaQwgTRLr8ri4=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
@@ -20,15 +20,14 @@ github.com/petermattis/goid v0.0.0-20220331194723-8ee3e6ded87a h1:VXRRto5GMJPNfB
 github.com/petermattis/goid v0.0.0-20220331194723-8ee3e6ded87a/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-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/qiniu/x v1.11.9 h1:IfQNdeNcK43Q1+b/LdrcqmWjlhxq051YVBnua8J2qN8=
+github.com/qiniu/x v1.11.9/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -49,8 +48,6 @@ 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.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
-golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
+golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
+golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
 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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/imp.go b/imp.go
index 059b2eb2..50d4dac9 100644
--- a/imp.go
+++ b/imp.go
@@ -20,6 +20,7 @@ import (
 	"go/token"
 	"go/types"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"strings"
 
@@ -86,21 +87,38 @@ func (p *Importer) Import(pkgPath string) (pkg *types.Package, err error) {
 			if err = p.genGoExtern(ret.Dir, false); err != nil {
 				return
 			}
+		case gopmod.PkgtStandard:
+			return p.impFrom.ImportFrom(pkgPath, p.gop.Root, 0)
 		}
 	}
 	return p.impFrom.Import(pkgPath)
 }
 
 func (p *Importer) genGoExtern(dir string, isExtern bool) (err error) {
-	genfile := filepath.Join(dir, autoGenFile)
-	if _, err = os.Lstat(genfile); err == nil { // has gop_autogen.go
+	gosum := filepath.Join(dir, "go.sum")
+	if _, err = os.Lstat(gosum); err == nil { // has go.sum
 		return
 	}
+
 	if isExtern {
 		os.Chmod(dir, modWritable)
 		defer os.Chmod(dir, modReadonly)
 	}
-	return genGoIn(dir, &Config{Gop: p.gop, Importer: p, Fset: p.fset}, false, false)
+
+	genfile := filepath.Join(dir, autoGenFile)
+	if _, err = os.Lstat(genfile); err != nil { // has gop_autogen.go
+		err = genGoIn(dir, &Config{Gop: p.gop, Importer: p, Fset: p.fset}, false, false)
+		if err != nil {
+			return
+		}
+	}
+
+	cmd := exec.Command("go", "mod", "tidy")
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	cmd.Dir = dir
+	err = cmd.Run()
+	return
 }
 
 // -----------------------------------------------------------------------------
diff --git a/load.go b/load.go
index 88ab6df9..8f432b43 100644
--- a/load.go
+++ b/load.go
@@ -18,6 +18,7 @@ package gop
 
 import (
 	"errors"
+	"fmt"
 	"go/token"
 	"go/types"
 	"io/fs"
@@ -79,7 +80,7 @@ func lookupPub(mod *gopmod.Module) func(pkgPath string) (pubfile string, err err
 
 // -----------------------------------------------------------------------------
 
-func LoadDir(dir string, conf *Config, genTestPkg bool) (out, test *gox.Package, err error) {
+func LoadDir(dir string, conf *Config, genTestPkg bool, promptGenGo ...bool) (out, test *gox.Package, err error) {
 	if conf == nil {
 		conf = new(Config)
 	}
@@ -104,6 +105,13 @@ func LoadDir(dir string, conf *Config, genTestPkg bool) (out, test *gox.Package,
 	if err != nil {
 		return
 	}
+	if len(pkgs) == 0 {
+		return nil, nil, syscall.ENOENT
+	}
+
+	if promptGenGo != nil && promptGenGo[0] {
+		fmt.Printf("GenGo %v ...\n", dir)
+	}
 
 	imp := conf.Importer
 	if imp == nil {
diff --git a/parser/parser.go b/parser/parser.go
index 9b8e6a3f..264a601d 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -1354,7 +1354,7 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr {
 
 func (p *parser) isCommand(x ast.Expr) bool {
 	switch x.(type) {
-	case *ast.Ident, *ast.SelectorExpr:
+	case *ast.Ident, *ast.SelectorExpr, *ast.ErrWrapExpr:
 	default:
 		return false
 	}
@@ -1908,6 +1908,9 @@ L:
 			} else {
 				break L
 			}
+		case token.NOT, token.QUESTION:
+			x = &ast.ErrWrapExpr{X: x, Tok: p.tok, TokPos: p.pos}
+			p.next()
 		default:
 			if allowCmd && p.isCommand(x) {
 				if lhs {
@@ -1923,6 +1926,18 @@ L:
 	return x
 }
 
+// parseErrWrapExpr: expr! expr? expr?:defval
+func (p *parser) parseErrWrapExpr(lhs, allowTuple, allowCmd bool) ast.Expr {
+	x := p.parsePrimaryExpr(lhs, allowTuple, allowCmd)
+	if expr, ok := x.(*ast.ErrWrapExpr); ok {
+		if p.tok == token.COLON {
+			p.next()
+			expr.Default = p.parseUnaryExpr(false, true, false)
+		}
+	}
+	return x
+}
+
 // If lhs is set and the result is an identifier, it is not resolved.
 func (p *parser) parseUnaryExpr(lhs, allowTuple, allowCmd bool) ast.Expr {
 	if p.trace {
@@ -1993,27 +2008,6 @@ func (p *parser) parseUnaryExpr(lhs, allowTuple, allowCmd bool) ast.Expr {
 	return p.parseErrWrapExpr(lhs, allowTuple, allowCmd)
 }
 
-// parseErrWrapExpr: expr! expr? expr?:defval
-func (p *parser) parseErrWrapExpr(lhs, allowTuple, allowCmd bool) ast.Expr {
-	x := p.parsePrimaryExpr(lhs, allowTuple, allowCmd)
-	switch p.tok {
-	case token.NOT: // expr!
-		expr := &ast.ErrWrapExpr{X: x, Tok: token.NOT, TokPos: p.pos}
-		p.next()
-		return expr
-	case token.QUESTION: // expr? expr?:defval
-		expr := &ast.ErrWrapExpr{X: x, Tok: token.QUESTION, TokPos: p.pos}
-		p.next()
-		if p.tok == token.COLON {
-			p.next()
-			expr.Default = p.parseUnaryExpr(false, true, false)
-		}
-		return expr
-	default:
-		return x
-	}
-}
-
 func (p *parser) tokPrec() (token.Token, int) {
 	tok := p.tok
 	if p.inRHS && tok == token.ASSIGN {
@@ -2747,12 +2741,14 @@ func (p *parser) parseForPhraseStmtPart(lhs []ast.Expr) *ast.ForPhraseStmt {
 	tokPos := p.expect(token.ARROW) // <-
 	x := p.parseExpr(false, false, true)
 	var cond ast.Expr
+	var ifPos token.Pos
 	if p.tok == token.IF || p.tok == token.COMMA {
+		ifPos = p.pos
 		p.next()
 		cond = p.parseExpr(false, false, false)
 	}
 
-	stmt := &ast.ForPhraseStmt{ForPhrase: &ast.ForPhrase{TokPos: tokPos, X: x, Cond: cond}}
+	stmt := &ast.ForPhraseStmt{ForPhrase: &ast.ForPhrase{TokPos: tokPos, X: x, IfPos: ifPos, Cond: cond}}
 	switch len(lhs) {
 	case 1:
 		stmt.Value = p.toIdent(lhs[0])
@@ -2796,11 +2792,13 @@ func (p *parser) parseForPhrase() *ast.ForPhrase { // for k, v <- container if c
 	x := p.parseExpr(false, false, true)
 	var init ast.Stmt
 	var cond ast.Expr
+	var ifPos token.Pos
 	if p.tok == token.IF || p.tok == token.COMMA { // `condition` or `init; condition`
+		ifPos = p.pos
 		p.next()
 		init, cond = p.parseForPhraseCond()
 	}
-	return &ast.ForPhrase{For: pos, Key: k, Value: v, TokPos: tokPos, X: x, Init: init, Cond: cond}
+	return &ast.ForPhrase{For: pos, Key: k, Value: v, TokPos: tokPos, X: x, IfPos: ifPos, Init: init, Cond: cond}
 }
 
 func (p *parser) parseForStmt() ast.Stmt {
diff --git a/tidy.go b/tidy.go
index 826ef003..e96e2031 100644
--- a/tidy.go
+++ b/tidy.go
@@ -24,56 +24,61 @@ import (
 	"github.com/goplus/mod/env"
 	"github.com/goplus/mod/gopmod"
 	"github.com/goplus/mod/modfetch"
+	"github.com/qiniu/x/errors"
 )
 
 func Tidy(dir string, gop *env.Gop) (err error) {
-	mod, err := gopmod.Load(dir, mod.GopModOnly)
+	modObj, err := gopmod.Load(dir, mod.GopModOnly)
 	if err != nil {
-		return
+		return errors.NewWith(err, `gopmod.Load(dir, mod.GopModOnly)`, -2, "gopmod.Load", dir, mod.GopModOnly)
 	}
 
-	modRoot := mod.Root()
-	depMods, err := GenDepMods(mod, modRoot, true)
+	modRoot := modObj.Root()
+	depMods, err := GenDepMods(modObj, modRoot, true)
 	if err != nil {
-		return
+		return errors.NewWith(err, `GenDepMods(modObj, modRoot, true)`, -2, "gop.GenDepMods", modObj, modRoot, true)
 	}
 
-	old := mod.DepMods()
+	old := modObj.DepMods()
 	for modPath := range old {
 		if _, ok := depMods[modPath]; !ok { // removed
-			mod.DropRequire(modPath)
+			modObj.DropRequire(modPath)
 		}
 	}
 	for modPath := range depMods {
 		if _, ok := old[modPath]; !ok { // added
 			if newMod, e := modfetch.Get(modPath); e != nil {
-				return e
+				return errors.NewWith(e, `modfetch.Get(modPath)`, -1, "modfetch.Get", modPath)
 			} else {
-				mod.AddRequire(newMod.Path, newMod.Version)
+				modObj.AddRequire(newMod.Path, newMod.Version)
 			}
 		}
 	}
 
-	mod.Cleanup()
-	err = mod.Save()
+	modObj.Cleanup()
+	err = modObj.Save()
 	if err != nil {
-		return
+		return errors.NewWith(err, `modObj.Save()`, -2, "(*gopmod.Module).Save")
 	}
 
-	err = genGoDir(modRoot, &Config{DontUpdateGoMod: true, Gop: gop}, true, true)
+	conf := &Config{DontUpdateGoMod: true, Gop: gop}
+	err = genGoDir(modRoot, conf, true, true)
 	if err != nil {
-		return
+		return errors.NewWith(err, `genGoDir(modRoot, conf, true, true)`, -2, "gop.genGoDir", modRoot, conf, true, true)
 	}
 
-	err = mod.UpdateGoMod(gop, true)
+	err = modObj.UpdateGoMod(gop, true)
 	if err != nil {
-		return
+		return errors.NewWith(err, `modObj.UpdateGoMod(gop, true)`, -2, "(*gopmod.Module).UpdateGoMod", gop, true)
 	}
 
 	cmd := exec.Command("go", "mod", "tidy")
 	cmd.Stdout = os.Stdout
 	cmd.Stderr = os.Stderr
-	cmd.Stdin = os.Stdin
 	cmd.Dir = modRoot
-	return cmd.Run()
+	err = cmd.Run()
+	if err != nil {
+		err = errors.NewWith(err, `cmd.Run()`, -2, "(*exec.Cmd).Run")
+	}
+	return
 }


Test patch:
diff --git a/cl/builtin_test.go b/cl/builtin_test.go
index 9d64a164..0b7236f2 100644
--- a/cl/builtin_test.go
+++ b/cl/builtin_test.go
@@ -47,7 +47,7 @@ func TestCompileErrWrapExpr(t *testing.T) {
 	}()
 	pkg := gox.NewPackage("", "foo", goxConf)
 	ctx := &blockCtx{pkg: pkg, cb: pkg.CB()}
-	compileErrWrapExpr(ctx, &ast.ErrWrapExpr{Tok: token.QUESTION})
+	compileErrWrapExpr(ctx, &ast.ErrWrapExpr{Tok: token.QUESTION}, 0)
 }
 
 func TestToString(t *testing.T) {
@@ -86,7 +86,7 @@ func TestHandleRecover(t *testing.T) {
 }
 
 func TestCanAutoCall(t *testing.T) {
-	if !canAutoCall(
+	if !isCommandWithoutArgs(
 		&ast.SelectorExpr{
 			X:   &ast.SelectorExpr{X: ast.NewIdent("foo"), Sel: ast.NewIdent("bar")},
 			Sel: ast.NewIdent("val"),
@@ -95,11 +95,26 @@ func TestCanAutoCall(t *testing.T) {
 	}
 }
 
+func TestClRangeStmt(t *testing.T) {
+	ctx := &blockCtx{
+		cb: &gox.CodeBuilder{},
+	}
+	stmt := &ast.RangeStmt{
+		Tok:  token.DEFINE,
+		X:    &ast.SliceLit{},
+		Body: &ast.BlockStmt{},
+	}
+	compileRangeStmt(ctx, stmt)
+	stmt.Tok = token.ASSIGN
+	stmt.Value = &ast.Ident{Name: "_"}
+	compileRangeStmt(ctx, stmt)
+}
+
 // -----------------------------------------------------------------------------
 
 func TestGmxSettings(t *testing.T) {
 	pkg := gox.NewPackage("", "foo", goxConf)
-	gmx := newGmx(pkg, "main.t2gmx", &Config{
+	gmx := newGmx(nil, pkg, "main.t2gmx", &Config{
 		LookupClass: lookupClass,
 	})
 	scheds := gmx.getScheds(pkg.CB())
@@ -110,6 +125,16 @@ func TestGmxSettings(t *testing.T) {
 	if gmx.getScheds(nil) != nil {
 		t.Fatal("TestGmxSettings failed: hasScheds?")
 	}
+	_, err := NewPackage("", &ast.Package{Files: map[string]*ast.File{
+		"main.t2gmx": {
+			IsProj: true,
+		},
+	}}, &Config{
+		LookupClass: lookupClassErr,
+	})
+	if e := err.Error(); e != `github.com/goplus/gop/cl/internal/libc.Gop_game not found` {
+		t.Fatal("newGmx:", e)
+	}
 }
 
 func TestSpxLookup(t *testing.T) {
@@ -131,6 +156,16 @@ func lookupClass(ext string) (c *gopmod.Class, ok bool) {
 	return
 }
 
+func lookupClassErr(ext string) (c *gopmod.Class, ok bool) {
+	switch ext {
+	case ".t2gmx", ".t2spx":
+		return &gopmod.Class{
+			ProjExt: ".t2gmx", WorkExt: ".t2spx",
+			PkgPaths: []string{"github.com/goplus/gop/cl/internal/libc"}}, true
+	}
+	return
+}
+
 func TestGetGoFile(t *testing.T) {
 	if f := getGoFile("a_test.gop", true); f != testingGoFile {
 		t.Fatal("TestGetGoFile:", f)
diff --git a/cl/compile_test.go b/cl/compile_test.go
index 7a3934ed..989d5506 100644
--- a/cl/compile_test.go
+++ b/cl/compile_test.go
@@ -103,6 +103,40 @@ func gopClTestFS(t *testing.T, conf *cl.Config, fs parser.FileSystem, pkgname, e
 	}
 }
 
+func TestVargCommand(t *testing.T) {
+	gopClTest(t, `
+type foo int
+
+func (f foo) Ls(args ...string) {
+}
+
+var f foo
+f.ls
+`, `package main
+
+type foo int
+
+func (f foo) Ls(args ...string) {
+}
+
+var f foo
+
+func main() {
+	f.Ls()
+}
+`)
+}
+
+func _TestCommandInPkg(t *testing.T) {
+	gopClTest(t, `
+func Ls(args ...string) {
+}
+
+ls
+`, `
+`)
+}
+
 func TestMixedGo(t *testing.T) {
 	gopMixedClTest(t, "main", `package main
 
@@ -146,6 +180,25 @@ var c foo
 var d int = c.v
 var e = foo3{}
 var x string = c.Str()
+`)
+	gopMixedClTest(t, "main", `package main
+type Point struct {
+	X int
+	Y int
+}
+`, `
+type T struct{}
+println(&T{},&Point{10,20})
+`, `package main
+
+import fmt "fmt"
+
+type T struct {
+}
+
+func main() {
+	fmt.Println(&T{}, &Point{10, 20})
+}
 `)
 }
 
@@ -1358,7 +1411,7 @@ func foo(script string) {
 `)
 }
 
-func TestErrWrap(t *testing.T) {
+func TestErrWrapBasic(t *testing.T) {
 	gopClTest(t, `
 import "strconv"
 
@@ -1444,6 +1497,56 @@ var ret int = func() (_gop_ret int) {
 `)
 }
 
+func TestErrWrapCommand(t *testing.T) {
+	gopClTest(t, `
+func mkdir(name string) error {
+	return nil
+}
+
+mkdir! "foo"
+`, `package main
+
+func mkdir(name string) error {
+	return nil
+}
+func main() {
+	func() {
+		var _gop_err error
+		_gop_err = mkdir("foo")
+		if _gop_err != nil {
+			panic(_gop_err)
+		}
+		return
+	}()
+}
+`)
+}
+
+func TestErrWrapCall(t *testing.T) {
+	gopClTest(t, `
+func foo() (func(), error) {
+	return nil, nil
+}
+
+foo()!()
+`, `package main
+
+func foo() (func(), error) {
+	return nil, nil
+}
+func main() {
+	func() (_gop_ret func()) {
+		var _gop_err error
+		_gop_ret, _gop_err = foo()
+		if _gop_err != nil {
+			panic(_gop_err)
+		}
+		return
+	}()()
+}
+`)
+}
+
 func TestMakeAndNew(t *testing.T) {
 	gopClTest(t, `
 var a *int = new(int)
@@ -2397,6 +2500,26 @@ func main() {
 `)
 }
 
+func TestSelectComprehensionRetTwoValue(t *testing.T) {
+	gopClTest(t, `
+func foo() (int, bool) {
+	return {i for i, x <- ["1", "3", "5", "7", "11"], x == "5"}
+}
+`, `package main
+
+func foo() (int, bool) {
+	return func() (_gop_ret int, _gop_ok bool) {
+		for i, x := range []string{"1", "3", "5", "7", "11"} {
+			if x == "5" {
+				return i, true
+			}
+		}
+		return
+	}()
+}
+`)
+}
+
 func TestListComprehension(t *testing.T) {
 	gopClTest(t, `
 a := [1, 3.4, 5]
diff --git a/cl/error_msg_test.go b/cl/error_msg_test.go
index 68c038c4..16385ee9 100644
--- a/cl/error_msg_test.go
+++ b/cl/error_msg_test.go
@@ -17,7 +17,10 @@
 package cl_test
 
 import (
+	"fmt"
 	"os"
+	"path/filepath"
+	"runtime"
 	"testing"
 
 	"github.com/goplus/gop/cl"
@@ -845,3 +848,21 @@ type A struct {
 }
 `)
 }
+
+func TestErrImportPkg(t *testing.T) {
+	codeErrorTest(t,
+		fmt.Sprintf(`./bar.gop:3:2: package fmt2 is not in GOROOT (%v)
+`, filepath.Join(runtime.GOROOT(), "src", "fmt2")), `
+import (
+	"fmt2"
+)
+`)
+
+	codeErrorTest(t, `./bar.gop:3:2: no required module provides package github.com/goplus/gop/fmt2; to add it:
+	go get github.com/goplus/gop/fmt2
+`, `
+import (
+	"github.com/goplus/gop/fmt2"
+)
+`)
+}
diff --git a/parser/_testdata/errwrap3/errwrap3.gop b/parser/_testdata/errwrap3/errwrap3.gop
new file mode 100644
index 00000000..024a5476
--- /dev/null
+++ b/parser/_testdata/errwrap3/errwrap3.gop
@@ -0,0 +1,2 @@
+mkdir! "foo"
+println foo()!.fields
diff --git a/parser/_testdata/errwrap3/parser.expect b/parser/_testdata/errwrap3/parser.expect
new file mode 100644
index 00000000..eaab0127
--- /dev/null
+++ b/parser/_testdata/errwrap3/parser.expect
@@ -0,0 +1,47 @@
+package main
+
+file errwrap3.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.ErrWrapExpr:
+                  X:
+                    ast.Ident:
+                      Name: mkdir
+                  Tok: !
+              Args:
+                ast.BasicLit:
+                  Kind: STRING
+                  Value: "foo"
+        ast.ExprStmt:
+          X:
+            ast.CallExpr:
+              Fun:
+                ast.Ident:
+                  Name: println
+              Args:
+                ast.SelectorExpr:
+                  X:
+                    ast.ErrWrapExpr:
+                      X:
+                        ast.CallExpr:
+                          Fun:
+                            ast.Ident:
+                              Name: foo
+                      Tok: !
+                  Sel:
+                    ast.Ident:
+                      Name: fields
diff --git a/parser/_testdata/fncall/fncall.gop b/parser/_testdata/fncall/fncall.gop
new file mode 100644
index 00000000..103721cc
--- /dev/null
+++ b/parser/_testdata/fncall/fncall.gop
@@ -0,0 +1,1 @@
+fn(1)(x)
diff --git a/parser/_testdata/fncall/parser.expect b/parser/_testdata/fncall/parser.expect
new file mode 100644
index 00000000..a740e409
--- /dev/null
+++ b/parser/_testdata/fncall/parser.expect
@@ -0,0 +1,30 @@
+package main
+
+file fncall.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.CallExpr:
+                  Fun:
+                    ast.Ident:
+                      Name: fn
+                  Args:
+                    ast.BasicLit:
+                      Kind: INT
+                      Value: 1
+              Args:
+                ast.Ident:
+                  Name: x