Repository: goplus/gop
Pull request #1249 (2022-06-06 05:55:37)

v1.1.0


Patch:
diff --git a/README.md b/README.md
index 0be12e91..317567f3 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,22 @@
 * **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.
+
+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 the project and present it in a low-code form? Go+ hopes to explore a new path at this point.
+
 ## How to install
 
 For now, we suggest you install Go+ from source code.
@@ -32,267 +48,686 @@ all.bat
 Actually, `all.bash` and `all.bat` will use `go run cmd/make.go` underneath.
 
 
-## Code style (important)
+## Running in Go+ playground
 
-* https://tutorial.goplus.org/hello-world
+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/
 
-## Summary about Go+
+And you can share your Go+ code with your friends.
+Here is my `Hello world` program:
+* https://play.goplus.org/p/AAh_gQAKAZR.
 
-What are mainly impressions about Go+?
 
-- A static typed language.
-- Fully compatible with [the Go language](https://github.com/golang/go).
-- Script-like style, and more readable code than Go.
+## Table of Contents
 
-For example, the following is legal Go+ source code:
+<table>
+    <tr><td width=33% valign=top>
 
-```coffee
-println [1, 2, 3.4]
+* [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)
+    * [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)
+
+</td><td valign=top>
+
+* [Functions](#functions)
+    * [Returning multiple values](#returning-multiple-values)
+    * [Variable number of arguments](#variable-number-of-arguments)
+    * [Higher order functions](#higher-order-functions)
+    * [Lambda expressions](#lambda-expressions)
+* [Structs](#structs)
+* [Error handling](#error-handling)
+* [Unix shebang](#unix-shebang)
+
+</td></tr>
+</table>
+
+
+## Hello World
+
+```go
+println "Hello world"
 ```
 
-How do we do this in the Go language?
+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
-package main
+import "os"
 
-import "fmt"
+println os.Args
+```
 
-func main() {
-    fmt.Println([]float64{1, 2, 3.4})
-}
+<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.
+*/
 ```
 
-Of course, we don't only do less-typing things.
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
 
-For example, we support [list comprehension](https://en.wikipedia.org/wiki/List_comprehension), which makes data processing easier.
+
+## Variables
 
 ```go
-println [x*x for x <- 1:6:2] // output: [1 9 25]
+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.
 
-mapData := {"Hi": 1, "Hello": 2, "Go+": 3}
-reversedMap := {v: k for k, v <- mapData}
-println reversedMap // output: map[1:Hi 2:Hello 3:Go+]
+```go failcompile
+age = 21
 ```
 
-We will keep Go+ simple. This is why we call it Go+, not Go++.
+This code will not compile, because the variable `age` is not declared.
+All variables need to be declared in Go+.
 
-Less is exponentially more.
+```go
+age := 21
+```
 
-It's for Go, and it's also for Go+.
+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
+```
 
-## Tutorials
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
 
-* https://tutorial.goplus.org/
 
+## Go+ Types
 
-## Playground
+### Primitive types
 
-Go+ Playground based on Docker:
-* https://play.goplus.org/
+```go ignore
+bool
 
-Go+ Playground based on GopherJS:
-* https://jsplay.goplus.org/
+int8    int16   int32   int    int64    int128
+uint8   uint16  uint32  uint   uint64   uint128
 
+uintptr // similar to C's size_t
 
-## Compatibility with Go
+byte // alias for uint8
+rune // alias for int32, represents a Unicode code point
 
-All Go features will be supported (including partially support `cgo`, see [below](#bytecode-vs-go-code)).
+string
 
-**All Go packages (even these packages use `cgo`) can be imported by Go+.**
+float32 float64
 
-```coffee
-import (
-    "fmt"
-    "strings"
-)
+complex64 complex128
 
-x := strings.NewReplacer("?", "!").Replace("hello, world???")
-fmt.Println "x:", x
+bigint bigrat
+
+unsafe.Pointer // similar to C's void*
+
+any // alias for Go's interface{}
 ```
 
-**And all Go+ packages can also be imported in Go programs. What you need to do is just using `gop` command instead of `go`.**
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
 
-First, let's make a directory named `14-Using-goplus-in-Go`.
+### Strings
 
-Then write a Go+ package named [foo](https://github.com/goplus/tutorial/tree/main/14-Using-goplus-in-Go/foo) in it:
+```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
-package foo
+s := "12"
+a, err := s.int
+b := s.int! // will panic if s isn't a valid integer
+```
 
-func ReverseMap(m map[string]int) map[int]string {
-    return {v: k for k, v <- m}
-}
+<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
 ```
 
-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):
+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
-package main
+age := 10
+println "age = " + age.string
+```
 
-import (
-    "fmt"
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
 
-    "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)
-}
+### Runes
+
+A `rune` represents a single Unicode character and is an alias for `int32`.
+
+```go
+rocket := '🚀'
+println rocket         // 128640
+println string(rocket) // 🚀
 ```
 
-How to build this example? You can use:
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
 
-```bash
-gop install -v ./...
+
+### Numbers
+
+```go
+a := 123
 ```
 
-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.
+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:
 
-## Bytecode vs. Go code
+```go
+a := 0x7B
+b := 0b01111011
+c := 0o173
+```
 
-Go+ supports bytecode backend and Go code generation.
+All of these will be assigned the same value, 123. They will all have type
+`int`, no matter what notation you used.
 
-When we use `gop` command, it generates Go code to covert Go+ package into Go packages.
+Go+ also supports writing numbers with `_` as separator:
 
-```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
+```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
 ```
 
-When we use [`igop`](https://github.com/goplus/igop) command, it generates bytecode to execute.
+If you want a different type of integer, you can use casting:
 
-```bash
-igop  # Run a Go+ program
+```go
+a := int64(123)
+b := uint8(12)
+c := int128(12345)
 ```
 
-In bytecode mode, Go+ doesn't support `cgo`. However, in Go-code-generation mode, Go+ fully supports `cgo`.
+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>
 
-## Go+ features
 
-### Rational number: bigint, bigrat, bigfloat
+#### Rational numbers
 
-We introduce the rational number 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>. And 4/5r means the rational constant 4/5.
+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
-var a bigint = 1r << 65  // bigint, large than int64
-var b bigrat = 4/5r      // bigrat
-c := b - 1/3r + 3 * 1/2r // bigrat
-println a, b, c
+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`.
 
-var x *big.Int = 1r << 65 // (1r << 65) is untyped bigint, and can be assigned to *big.Int
-var y *big.Rat = 4/5r
-println x, y
+```go
+a := 4/5r
+b := a - 1/3r + 3 * 1/2r
+println a, b // 4/5 59/30
 ```
 
-### Large integer: uint128, int128
+Casting rational numbers works the same way:
 
 ```go
-var x uint128 = 1 << 65
-var y = x + 1
-println x // output: 36893488147419103232
-println y // output: 36893488147419103233
+a := 1r
+b := bigrat(1r)
+c := bigrat(1)
+println a/3 // 0
+println b/3 // 1/3
+println c/3 // 1/3
 ```
 
-### Convert bool to number types
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+#### Convert bool to number types
 
 ```go
-println int(true)       // output: 1
-println float64(true)   // output: 1
-println complex64(true) // output: (1+0i)
+println int(true)       // 1
+println float64(true)   // 1
+println complex64(true) // (1+0i)
 ```
 
-### Map literal
+<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
-x := {"Hello": 1, "xsw": 3.4}   // map[string]float64
-y := {"Hello": 1, "xsw": "Go+"} // map[string]interface{}
-z := {"Hello": 1, "xsw": 3}     // map[string]int
-empty := {}                     // map[string]interface{}
+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]
 ```
 
-### Slice literal
+Type of a slice literal is infered automatically.
 
 ```go
-x := [1, 3.4]       // []float64
-y := [1]            // []int
-z := [1+2i, "xsw"]  // []interface{}
-a := [1, 3.4, 3+4i] // []complex128
-b := [5+6i]         // []complex128
-c := ["xsw", 3]     // []interface{}
-empty := []         // []interface{}
+a := [1, 2, 3]   // []int
+b := [1, 2, 3.4] // []float64
+c := ["Hi"]      // []string
+d := ["Hi", 10]  // []any
+d := []          // []any
 ```
 
-### Lambda expression
+And casting slices also works.
 
 ```go
-func plot(fn func(x float64) float64) {
-    // ...
+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
 }
+```
 
-func plot2(fn func(x float64) (float64, float64)) {
-    // ...
+<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.
 
-plot x => x * x           // plot(func(x float64) float64 { return x * x })
-plot2 x => (x * x, x + x) // plot2(func(x float64) (float64, float64) { return x * x, x + x })
+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
 ```
 
-### Deduce struct type
+If an index is required, an alternative form `for index, value <- arr` can be used.
 
 ```go
-type Config struct {
-    Dir   string
-    Level int
+names := ["Sam", "Peter"]
+for i, name <- names {
+    println i, name
+    // 0 Sam
+    // 1 Peter
 }
+```
 
-func foo(conf *Config) {
-    // ...
+<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
+}
+```
 
-foo {Dir: "/foo/bar", Level: 1}
+<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
+}
 ```
 
-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.
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
 
-You also can omit struct types in a return statement. For example:
+
+##### `for`/`<-`/`if`
+
+All loops of `for`/`<-` form can have an optional `if` condition.
 
 ```go
-type Result struct {
-    Text string
+numbers := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+for num <- numbers if num%3 == 0 {
+    println num
+    // 0
+    // 3
+    // 6
+    // 9
 }
 
-func foo() *Result {
-    return {Text: "Hi, Go+"} // return &Result{Text: "Hi, Go+"}
+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>
+
 
 ### List comprehension
 
 ```go
 a := [x*x for x <- [1, 3, 5, 7, 11]]
-b := [x*x for x <- [1, 3, 5, 7, 11], x > 3]
-c := [i+v for i, v <- [1, 3, 5, 7, 11], i%2 == 1]
-d := [k+","+s for k, s <- {"Hello": "xsw", "Hi": "Go+"}]
+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]
-e := [[a, b] for a <- arr, a < b for b <- arr, b > 2]
+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], i%2 == 1}
-z := {v: k for k, v <- {1: "Hello", 3: "Hi", 5: "xsw", 7: "Go+"}, k > 3}
+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
@@ -303,13 +738,16 @@ type student struct {
 
 students := [student{"Ken", 90}, student{"Jason", 80}, student{"Lily", 85}]
 
-unknownScore, ok := {x.score for x <- students, x.name == "Unknown"}
-jasonScore := {x.score for x <- students, x.name == "Jason"}
+unknownScore, ok := {x.score for x <- students if x.name == "Unknown"}
+jasonScore := {x.score for x <- students if x.name == "Jason"}
 
-println unknownScore, ok // output: 0 false
-println jasonScore // output: 80
+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
@@ -320,42 +758,121 @@ type student struct {
 
 students := [student{"Ken", 90}, student{"Jason", 80}, student{"Lily", 85}]
 
-hasJason := {for x <- students, x.name == "Jason"} // is any student named Jason?
-hasFailed := {for x <- students, x.score < 60}     // is any student failed?
+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?
 ```
 
-### For loop
+<h5 align="right"><a href="#table-of-contents">⬆ back to toc</a></h5>
+
+
+## Functions
 
 ```go
-sum := 0
-for x <- [1, 3, 5, 7, 11, 13, 17], x > 3 {
-    sum += x
+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>
+
 
-### Range expression (`start:end:step`)
+### Returning multiple values
 
 ```go
-for i <- :10 {
-    println i
+func foo() (int, int) {
+	return 2, 3
 }
 
-for i := range :10:2 {
-    println i
+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>
+
+
+### Variable number of arguments
+
+```go
+func sum(a ...int) int {
+    total := 0
+	for x <- a {
+		total += x
+	}
+	return total
 }
 
-for i := range 1:10:3 {
-    println i
+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 transform(a []float64, f func(float64) float64) []float64 {
+    return [f(x) for x <- a]
 }
 
-for range :10 {
-    println "Range expression"
+y := transform([1, 2, 3], square)
+println y // [1 4 9]
+```
+
+<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
 
-### For range of UDT
+### Custom iterators
+
+#### For range of UDT
 
 ```go
 type Foo struct {
@@ -381,8 +898,7 @@ 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).**
 
-
-### For range of UDT2
+#### For range of UDT2
 
 ```go
 type FooIter struct {
@@ -414,6 +930,35 @@ for k, v <- foo {
 println {v: k for k, v <- foo}
 ```
 
+### 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+"}
+}
+```
+
 ### Overload operators
 
 ```go
@@ -445,8 +990,34 @@ println a + Int(3r)
 println -a
 ```
 
+### 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.
+
 
-### Error handling
+## Error handling
 
 We reinvent the error handling specification in Go+. We call them `ErrWrap expressions`:
 
@@ -499,62 +1070,111 @@ And the most interesting thing is, the return error contains the full error stac
 How these `ErrWrap expressions` work? See [Error Handling](https://github.com/goplus/gop/wiki/Error-Handling) for more information.
 
 
-### Auto property
+## Unix shebang
 
-Let's see an example written in Go+:
+You can use Go+ programs as shell scripts now. For example:
 
 ```go
-import "gop/ast/goptest"
+#!/usr/bin/env -S gop run
 
-doc := goptest.New(`... Go+ code ...`)!
+println "Hello, Go+"
 
-println doc.Any().FuncDecl().Name()
+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]
 ```
 
-In many languages, there is a concept named `property` who has `get` and `set` methods.
+Go [20-Unix-Shebang/shebang](https://github.com/goplus/tutorial/blob/main/20-Unix-Shebang/shebang) to get the source code.
 
-Suppose we have `get property`, the above example will be:
 
-```go
-import "gop/ast/goptest"
+## Compatibility with Go
 
-doc := goptest.New(`... Go+ code ...`)!
+All Go features will be supported (including partially support `cgo`, see [below](#bytecode-vs-go-code)).
 
-println doc.any.funcDecl.name
+**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
 ```
 
-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.
+**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:
 
-### Unix shebang
+```go
+package foo
 
-You can use Go+ programs as shell scripts now. For example:
+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
-#!/usr/bin/env -S gop run
+package main
 
-println "Hello, Go+"
+import (
+    "fmt"
 
-println 1r << 129
-println 1/3r + 2/7r*2
+    "github.com/goplus/tutorial/14-Using-goplus-in-Go/foo"
+)
 
-arr := [1, 3, 5, 7, 11, 13, 17, 19]
-println arr
-println [x*x for x <- arr, x > 3]
+func main() {
+    rmap := foo.ReverseMap(map[string]int{"Hi": 1, "Hello": 2})
+    fmt.Println(rmap)
+}
+```
 
-m := {"Hi": 1, "Go+": 2}
-println m
-println {v: k for k, v <- m}
-println [k for k, _ <- m]
-println [v for v <- m]
+How to build this example? You can use:
+
+```bash
+gop install -v ./...
 ```
 
-Go [20-Unix-Shebang/shebang](https://github.com/goplus/tutorial/blob/main/20-Unix-Shebang/shebang) to get the source code.
+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.
 
 
-### Go features
+## Bytecode vs. Go code
+
+Go+ supports bytecode backend and Go code generation.
 
-All Go features (including partially support `cgo`) will be supported. In bytecode mode, Go+ doesn't support `cgo`. However, in Go-code-generation mode, Go+ fully supports `cgo`.
+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`.
 
 
 ## IDE Plugins
@@ -562,6 +1182,11 @@ All Go features (including partially support `cgo`) will be supported. In byteco
 * vscode: https://github.com/gopcode/vscode-goplus
 
 
+## Tutorials
+
+* https://tutorial.goplus.org/
+
+
 ## Contributing
 
 The Go+ project welcomes all contributors. We appreciate your help!
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/cl/expr.go b/cl/expr.go
index 8e8fed49..80e9fb42 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -403,6 +403,7 @@ type fnType struct {
 	params   *types.Tuple
 	n1       int
 	variadic bool
+	typetype bool
 	inited   bool
 }
 
@@ -428,12 +429,20 @@ func (p *fnType) init(t *types.Signature) {
 	}
 }
 
+func (p *fnType) initTypeType(t *gox.TypeType) {
+	param := types.NewParam(0, nil, "", t.Type())
+	p.params, p.typetype = types.NewTuple(param), true
+	p.n1 = 1
+}
+
 func (p *fnType) initWith(fnt types.Type, idx, nin int) {
 	if p.inited {
 		return
 	}
 	p.inited = true
-	if t := gox.CheckSignature(fnt, idx, nin); t != nil {
+	if t, ok := fnt.(*gox.TypeType); ok {
+		p.initTypeType(t)
+	} else if t := gox.CheckSignature(fnt, idx, nin); t != nil {
 		p.init(t)
 	}
 }
@@ -470,6 +479,26 @@ func compileCallExpr(ctx *blockCtx, v *ast.CallExpr, inFlags int) {
 		case *ast.CompositeLit:
 			fn.initWith(fnt, i, -1)
 			compileCompositeLit(ctx, expr, fn.arg(i, ellipsis), true)
+		case *ast.SliceLit:
+			fn.initWith(fnt, i, -2)
+			t := fn.arg(i, ellipsis)
+			switch t.(type) {
+			case *types.Slice:
+			case *types.Named:
+				if _, ok := getUnderlying(ctx, t).(*types.Slice); !ok {
+					t = nil
+				}
+			default:
+				t = nil
+			}
+			typetype := fn.typetype && t != nil && len(v.Args) == 1
+			if typetype {
+				ctx.cb.InternalStack().Pop()
+			}
+			compileSliceLit(ctx, expr, t)
+			if typetype {
+				return
+			}
 		default:
 			compileExpr(ctx, arg)
 		}
diff --git a/cl/stmt.go b/cl/stmt.go
index ce318822..c788fa99 100644
--- a/cl/stmt.go
+++ b/cl/stmt.go
@@ -258,7 +258,7 @@ func compileRangeStmt(ctx *blockCtx, v *ast.RangeStmt) {
 		if v.Tok == token.ASSIGN {
 			tok = v.Tok
 		}
-		compileForStmt(ctx, toForStmt(v.For, v.Key, v.Body, re, tok))
+		compileForStmt(ctx, toForStmt(v.For, v.Key, v.Body, re, tok, nil))
 		return
 	}
 	cb := ctx.cb
@@ -306,7 +306,7 @@ func compileRangeStmt(ctx *blockCtx, v *ast.RangeStmt) {
 
 func compileForPhraseStmt(ctx *blockCtx, v *ast.ForPhraseStmt) {
 	if re, ok := v.X.(*ast.RangeExpr); ok {
-		compileForStmt(ctx, toForStmt(v.For, v.Value, v.Body, re, token.DEFINE))
+		compileForStmt(ctx, toForStmt(v.For, v.Value, v.Body, re, token.DEFINE, v.ForPhrase))
 		return
 	}
 	cb := ctx.cb
@@ -338,7 +338,7 @@ func compileForPhraseStmt(ctx *blockCtx, v *ast.ForPhraseStmt) {
 	cb.End()
 }
 
-func toForStmt(forPos token.Pos, value ast.Expr, body *ast.BlockStmt, re *ast.RangeExpr, tok token.Token) *ast.ForStmt {
+func toForStmt(forPos token.Pos, value ast.Expr, body *ast.BlockStmt, re *ast.RangeExpr, tok token.Token, fp *ast.ForPhrase) *ast.ForStmt {
 	nilIdent := value == nil
 	if !nilIdent {
 		if v, ok := value.(*ast.Ident); ok && v.Name == "_" {
@@ -348,11 +348,12 @@ func toForStmt(forPos token.Pos, value ast.Expr, body *ast.BlockStmt, re *ast.Ra
 	if nilIdent {
 		value = &ast.Ident{NamePos: forPos, Name: "_gop_k"}
 	}
-	if re.First == nil {
-		re.First = &ast.BasicLit{ValuePos: forPos, Kind: token.INT, Value: "0"}
+	first := re.First
+	if first == nil {
+		first = &ast.BasicLit{ValuePos: forPos, Kind: token.INT, Value: "0"}
 	}
 	initLhs := []ast.Expr{value}
-	initRhs := []ast.Expr{re.First}
+	initRhs := []ast.Expr{first}
 	replaceValue := false
 	var cond ast.Expr
 	var post ast.Expr
@@ -390,6 +391,16 @@ func toForStmt(forPos token.Pos, value ast.Expr, body *ast.BlockStmt, re *ast.Ra
 		}}, body.List...)
 		tok = token.DEFINE
 	}
+	if fp != nil && fp.Cond != nil {
+		condStmt := &ast.IfStmt{
+			Init: fp.Init,
+			Cond: fp.Cond,
+			Body: body,
+		}
+		body = &ast.BlockStmt{
+			List: []ast.Stmt{condStmt},
+		}
+	}
 	return &ast.ForStmt{
 		For: forPos,
 		Init: &ast.AssignStmt{
diff --git a/parser/parser.go b/parser/parser.go
index 6985133e..9b8e6a3f 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -813,7 +813,7 @@ func (p *parser) parseArrayTypeOrSliceLit(state int, slice ast.Expr) (expr ast.E
 				sliceLit := p.parseSliceLit(lbrack, len)
 				p.exprLev--
 				return sliceLit, resultSliceLit
-			case token.FOR: // [expr for k, v <- container, cond]
+			case token.FOR: // [expr for k, v <- container if cond]
 				phrases := p.parseForPhrases()
 				p.exprLev--
 				rbrack := p.expect(token.RBRACK)
@@ -2747,7 +2747,7 @@ func (p *parser) parseForPhraseStmtPart(lhs []ast.Expr) *ast.ForPhraseStmt {
 	tokPos := p.expect(token.ARROW) // <-
 	x := p.parseExpr(false, false, true)
 	var cond ast.Expr
-	if p.tok == token.COMMA {
+	if p.tok == token.IF || p.tok == token.COMMA {
 		p.next()
 		cond = p.parseExpr(false, false, false)
 	}
@@ -2776,7 +2776,7 @@ func (p *parser) toIdent(e ast.Expr) *ast.Ident {
 	return nil
 }
 
-func (p *parser) parseForPhrase() *ast.ForPhrase { // for k, v <- container, cond
+func (p *parser) parseForPhrase() *ast.ForPhrase { // for k, v <- container if cond
 	if p.trace {
 		defer un(trace(p, "ForPhrase"))
 	}
@@ -2796,7 +2796,7 @@ func (p *parser) parseForPhrase() *ast.ForPhrase { // for k, v <- container, con
 	x := p.parseExpr(false, false, true)
 	var init ast.Stmt
 	var cond ast.Expr
-	if p.tok == token.COMMA { // `condition` or `init; condition`
+	if p.tok == token.IF || p.tok == token.COMMA { // `condition` or `init; condition`
 		p.next()
 		init, cond = p.parseForPhraseCond()
 	}
diff --git a/printer/nodes.go b/printer/nodes.go
index ce9b8676..0cdd255d 100644
--- a/printer/nodes.go
+++ b/printer/nodes.go
@@ -1112,7 +1112,7 @@ func (p *printer) listForPhrase(prev0 token.Pos, list []*ast.ForPhrase, depth in
 		p.print(x.TokPos, token.ARROW, blank)
 		p.expr(x.X)
 		if x.Cond != nil {
-			p.print(x.Cond.Pos(), token.COMMA, blank)
+			p.print(blank, x.Cond.Pos(), token.IF, blank)
 			if x.Init != nil {
 				p.stmt(x.Init, false)
 				p.print(token.SEMICOLON, blank)
@@ -1515,7 +1515,7 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) {
 		p.print(blank, s.TokPos, token.ARROW, blank)
 		p.expr(s.X)
 		if s.Cond != nil {
-			p.print(s.Cond.Pos(), token.COMMA, blank)
+			p.print(blank, s.Cond.Pos(), token.IF, blank)
 			p.expr(s.Cond)
 		}
 		p.print(blank)


Test patch:
diff --git a/cl/compile_test.go b/cl/compile_test.go
index 0d886cdf..7a3934ed 100644
--- a/cl/compile_test.go
+++ b/cl/compile_test.go
@@ -149,6 +149,50 @@ var x string = c.Str()
 `)
 }
 
+func Test_RangeExpressionIf_Issue1243(t *testing.T) {
+	gopClTest(t, `
+for i <- :10, i%3 == 0 {
+	println i
+}`, `package main
+
+import fmt "fmt"
+
+func main() {
+	for i := 0; i < 10; i += 1 {
+		if i%3 == 0 {
+			fmt.Println(i)
+		}
+	}
+}
+`)
+}
+
+func Test_CastSlice_Issue1240(t *testing.T) {
+	gopClTest(t, `
+type fvec []float64
+type foo float64
+a := []float64([1, 2])
+b := fvec([1, 2])
+c := foo([1, 2])
+d := fvec([])
+println a, b, c, d
+`, `package main
+
+import fmt "fmt"
+
+type fvec []float64
+type foo float64
+
+func main() {
+	a := []float64{1, 2}
+	b := fvec{1, 2}
+	c := foo([]int{1, 2})
+	d := fvec{}
+	fmt.Println(a, b, c, d)
+}
+`)
+}
+
 func TestUnderscoreRedeclared_Issue1197(t *testing.T) {
 	gopClTest(t, `
 func() (_ [2]int) { type _ int; return }()
diff --git a/parser/_testdata/exists/exists.gop b/parser/_testdata/exists/exists.gop
index d846527f..7ebe18fd 100644
--- a/parser/_testdata/exists/exists.gop
+++ b/parser/_testdata/exists/exists.gop
@@ -1,2 +1,2 @@
 a := [1, 3, 5, 7, 8, 19]
-hasEven := {for x <- a, x%2 == 0}
+hasEven := {for x <- a if x%2 == 0}
diff --git a/parser/_testdata/forloop/forloop.gop b/parser/_testdata/forloop/forloop.gop
index 865fa4fd..e2cc6920 100644
--- a/parser/_testdata/forloop/forloop.gop
+++ b/parser/_testdata/forloop/forloop.gop
@@ -25,6 +25,6 @@ for x <- [1] {
 	println(x)
 }
 
-for i, x <- [1], i%2 == 0 {
+for i, x <- [1] if i%2 == 0 {
 	println(i, x)
 }
diff --git a/parser/_testdata/listcompr/listcompr.gop b/parser/_testdata/listcompr/listcompr.gop
index 5c8a5fe1..e7f61460 100644
--- a/parser/_testdata/listcompr/listcompr.gop
+++ b/parser/_testdata/listcompr/listcompr.gop
@@ -1,14 +1,14 @@
 y := [x*x for x <- [1]]
 println(y)
 
-y = [x*x for x <- [1, 3, 5, 7, 11], x > 3]
+y = [x*x for x <- [1, 3, 5, 7, 11] if x > 3]
 println(y)
 
-z := [i+v for i, v <- [1, 3, 5, 7, 11], t := i % 2; t == 1]
+z := [i+v for i, v <- [1, 3, 5, 7, 11] if t := i % 2; t == 1]
 println(z)
 
 println([k+","+s for k, s <- {"Hello": "xsw", "Hi": "Go+"}])
 
 arr := [1, 2, 3, 4, 5, 6]
-x := [[a, b] for a <- arr, a < b for b <- arr, b > 2]
+x := [[a, b] for a <- arr if a < b for b <- arr if b > 2]
 println("x:", x)
diff --git a/parser/_testdata/selectdata/select.gop b/parser/_testdata/selectdata/select.gop
index 7034a292..c5880fe6 100644
--- a/parser/_testdata/selectdata/select.gop
+++ b/parser/_testdata/selectdata/select.gop
@@ -1,2 +1,2 @@
 a := [1, 3, 5, 7, 8, 19]
-y := {x for x <- a, x%2 == 0}
+y := {x for x <- a if x%2 == 0}
diff --git a/printer/_testdata/10-List-comprehension/list_comprehens.gop b/printer/_testdata/10-List-comprehension/list_comprehens.gop
index cacd8530..7f6e6724 100644
--- a/printer/_testdata/10-List-comprehension/list_comprehens.gop
+++ b/printer/_testdata/10-List-comprehension/list_comprehens.gop
@@ -1,14 +1,14 @@
 y := [x*x for x <- [1, 3, 5, 7, 11]]
 println(y)
 
-y = [x*x for x <- [1, 3, 5, 7, 11], x > 3]
+y = [x*x for x <- [1, 3, 5, 7, 11] if x > 3]
 println(y)
 
-z := [i+v for i, v <- [1, 3, 5, 7, 11], i%2 == 1]
+z := [i+v for i, v <- [1, 3, 5, 7, 11] if i%2 == 1]
 println(z)
 
 println([k+","+s for k, s <- {"Hello": "xsw", "Hi": "Go+"}])
 
 arr := [1, 2, 3, 4, 5, 6]
-x := [[a, b] for a <- arr, a < b for b <- arr, b > 2]
+x := [[a, b] for a <- arr if a < b for b <- arr if b > 2]
 println("x:", x)
diff --git a/printer/_testdata/11-Map-comprehension/map_comprehens.gop b/printer/_testdata/11-Map-comprehension/map_comprehens.gop
index 684cbf0e..23bd1f13 100644
--- a/printer/_testdata/11-Map-comprehension/map_comprehens.gop
+++ b/printer/_testdata/11-Map-comprehension/map_comprehens.gop
@@ -1,8 +1,8 @@
 y := {x: i for i, x <- [1, 3, 5, 7, 11]}
 println(y)
 
-y = {x: i for i, x <- [1, 3, 5, 7, 11], i%2 == 1}
+y = {x: i for i, x <- [1, 3, 5, 7, 11] if i%2 == 1}
 println(y)
 
-z := {v: k for k, v <- {1: "Hello", 3: "Hi", 5: "xsw", 7: "Go+"}, k > 3}
+z := {v: k for k, v <- {1: "Hello", 3: "Hi", 5: "xsw", 7: "Go+"} if k > 3}
 println(z)
diff --git a/printer/_testdata/12-Select-comprehension/select.gop b/printer/_testdata/12-Select-comprehension/select.gop
index 25e80401..cc9ee6ad 100644
--- a/printer/_testdata/12-Select-comprehension/select.gop
+++ b/printer/_testdata/12-Select-comprehension/select.gop
@@ -1,8 +1,8 @@
 a := [2, 3, 5, 7, 9, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59]
-where := {i for i, v <- a, v == 19}
+where := {i for i, v <- a if v == 19}
 println("19 is at index", where)
 
-if at, ok := {i for i, v <- a, v == 37}; ok {
+if at, ok := {i for i, v <- a if v == 37}; ok {
 	println("37 is found! index =", at)
 }
 
diff --git a/printer/_testdata/12-Select-comprehension2/findscore.gop b/printer/_testdata/12-Select-comprehension2/findscore.gop
index 3ba439a1..a7845a1d 100644
--- a/printer/_testdata/12-Select-comprehension2/findscore.gop
+++ b/printer/_testdata/12-Select-comprehension2/findscore.gop
@@ -4,7 +4,7 @@ type student struct {
 }
 
 func findScore(a []student, name string) (score int, ok bool) {
-	return {x.score for x <- a, x.name == name}
+	return {x.score for x <- a if x.name == name}
 }
 
 a := [student{"ken", 95}, student{"john", 90}, student{"tom", 58}]
diff --git a/printer/_testdata/13-Exists-comprehension/exists.gop b/printer/_testdata/13-Exists-comprehension/exists.gop
index 55e31657..2e4cd1bb 100644
--- a/printer/_testdata/13-Exists-comprehension/exists.gop
+++ b/printer/_testdata/13-Exists-comprehension/exists.gop
@@ -5,8 +5,8 @@ type student struct {
 
 a := [student{"du", 84}, student{"wang", 70}, student{"ken", 100}]
 
-hasFullMark := {for x <- a, x.score == 100}
+hasFullMark := {for x <- a if x.score == 100}
 println("is any student full mark:", hasFullMark)
 
-hasFailed := {for x <- a, x.score < 60}
+hasFailed := {for x <- a if x.score < 60}
 println("is any student failed:", hasFailed)
diff --git a/printer/_testdata/22-For-loop/for.gop b/printer/_testdata/22-For-loop/for.gop
index 5febb6fc..4eb17153 100644
--- a/printer/_testdata/22-For-loop/for.gop
+++ b/printer/_testdata/22-For-loop/for.gop
@@ -2,7 +2,7 @@
 // for with <-
 
 sum := 0
-for x <- [1, 3, 5, 7, 11, 13, 17], x > 3 {
+for x <- [1, 3, 5, 7, 11, 13, 17] if x > 3 {
 	sum += x
 }
 println("sum(5,7,11,13,17):", sum)