16 | //
17 | // The example shows variables of several types, and also that variable declarations may be "factored" into blocks, as with import statements.
18 |
19 | import (
20 | "math/cmplx"
21 | )
22 |
23 | var (
24 | ToBe bool = false
25 | MaxInt uint64 = 1<<64 - 1
26 | z complex128 = cmplx.sqrt(-5 + 12i)
27 | )
28 |
29 | printf "Type: %T Value: %v\n", ToBe, ToBe
30 | printf "Type: %T Value: %v\n", MaxInt, MaxInt
31 | printf "Type: %T Value: %v\n", z, z
32 |
33 | //The int, uint, and uintptr types are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems. When you need an integer value you should use int unless you have a specific reason to use a sized or unsigned integer type.
34 |
--------------------------------------------------------------------------------
/107-Integers/integers.gop:
--------------------------------------------------------------------------------
1 | // int type represents a whole number, which can be positive or negative. The
2 | // int type size is platform-dependent and will be either 32 or 64 bits. There are
3 | // also integer types that have a specific size, such as int8, int16, int32, int64, and
4 | // int128, but the int type should be used unless you need a specific size.
5 | //
6 | // uint type represents a positive whole number. The uint type size is platformdependent and will be either 32 or 64 bits. There are also unsigned integer
7 | // types that have a specific size, such as uint8, uint16, uint32, uint64 and uint128, but
8 | // the uint type should be used unless you need a specific size.
9 | //
10 | // For int 20 values can also be expressed in hex (0x14), octal (0o24), and binary notation (0b0010100).
11 | // uint, there are no uint literals. All literal whole numbers are treated as int values.
12 | //
13 | // XGo also supports writing numbers with _ as separator and also support cast bool to number types.
14 | // As example shows
15 |
16 | num := 1_000_000 //XGo support, same as 1000000
17 | println num
18 |
19 | println int(true) //XGo support cast bool to int
20 | println float64(true) //and to float64
21 | println complex64(true) //and to complex64, and so on.
22 |
23 | println 20+20
24 | println 20+30
25 | println 0x14 //in hex
26 | println 0o24 //in octal
27 | println 0b0010100 // binary
28 |
29 | c := int128(12345) // If you want a different type of integer, you can use casting.
30 | println c
31 |
32 | u128 := uint128(12345)
33 | println(u128)
34 |
--------------------------------------------------------------------------------
/108-Floating-Point-Numbers/numbers.gop:
--------------------------------------------------------------------------------
1 | // There are two floating point types in XGo, float32 and float64
2 | // Floating point literals have a default type of float64.
3 | //
4 | // A floating-point number cannot represent a decimal value exactly. Do not use them to
5 | // represent money or any other value that must have an exact decimal representation!
6 | //
7 | // While you can use == and != to compare floats, don’t do it. Due to the
8 | // inexact nature of floats, two floating point values might not be equal when you
9 | // think they should be. Instead, define a minimum allowed variance and see if the
10 | // difference between two floats is less than that. This minimum value (sometimes
11 | // called epsilon) depends on what your accuracy needs are;
12 | //
13 | // Float literals can also be declared as a power of ten and dividing a float variable set to 0 by 0 returns NaN (Not a Number).
14 |
15 | f0 := 42e1 // 420
16 | f1 := 123e-2 // 1.23
17 | f2 := 456e+2 // 45600
18 | f3 := 1.0
19 | println(f0, f1, f2, f3, f0/0)
20 |
--------------------------------------------------------------------------------
/109-Complex-Numbers/complex-1.gop:
--------------------------------------------------------------------------------
1 | # Initializing Complex Numbers
2 | // There are two complex types in XGo. The complex64 and the complex128.
3 | // Initializing complex numbers is really easy. You can use the constructor or the initialization syntax as well.
4 |
5 | c1 := complex(10, 11) // constructor init
6 | c2 := 10 + 11i // complex number init syntax
7 | println c1, c2
8 |
--------------------------------------------------------------------------------
/109-Complex-Numbers/complex-2.gop:
--------------------------------------------------------------------------------
1 | # Parts of a Complex Number
2 | // There are two parts of a complex number. The real and imaginary part. We use functions to get those.
3 |
4 | cc := complex(23, 31)
5 | realPart := real(cc) // gets real part
6 | imagPart := imag(cc) // gets imaginary part
7 | println realPart, imagPart
8 |
--------------------------------------------------------------------------------
/109-Complex-Numbers/complex-3.gop:
--------------------------------------------------------------------------------
1 | # Operations on a Complex Number
2 | // A complex variable can do any operation like addition, subtraction, multiplication, and division.
3 | //
4 | // Let’s see an example to perform mathematical operations on the complex numbers.
5 |
6 | c11 := complex(10, 11) // constructor init
7 | c22 := 10 + 11i // complex number init syntax
8 |
9 | ccc := complex(2, 3)
10 | cc2 := 4 + 5i // complex initializer syntax a + ib
11 | cc3 := c11 + c22 // addition just like other variables
12 | println "Add: ", cc3 // prints "Add: (6+8i)"
13 | re := real(cc3) // get real part
14 | im := imag(cc3) // get imaginary part
15 | println ccc, cc2, re, im // prints 6 8
16 |
--------------------------------------------------------------------------------
/110-Booleans/boolean-1.gop:
--------------------------------------------------------------------------------
1 | // The bool type represents Boolean variables. Variables of bool type can have
2 | // one of two values: true or false. The zero value for a bool is false.
3 |
4 | var flag bool // no value assigned, set to false
5 | var isAwesome = true
6 |
7 | println(flag, isAwesome)
8 |
--------------------------------------------------------------------------------
/110-Booleans/boolean-2.gop:
--------------------------------------------------------------------------------
1 | // In XGo, you can cast bool to number types.
2 |
3 | println int(true) // 1
4 | println float64(true) // 1
5 | println complex64(true) // (1+0i)
6 |
--------------------------------------------------------------------------------
/111-Strings/strings.gop:
--------------------------------------------------------------------------------
1 | // A string is a sequence of bytes that cannot be changed. Strings can contain any data
2 | // and are generally used to save text.
3 | //
4 | // We generally use double quotes "" to define a string, but note that there are specific
5 | // characters that have a specific meaning, which we call escaped characters, and these
6 | // escaped characters contain:
7 | //
8 | //
15 |
16 | println("hello" + "\t" + "world") // hello world
17 |
18 | // If we want to know the length of the strings taken up by bytes, we can use XGo's built-in
19 | // functions to calculate it:
20 |
21 | println(len("helloword")) // 9
22 |
23 | // If we were to define a string, the grammar would be as follows:
24 |
25 | str := "helloword"
26 | println(str) // helloword
27 | println(len(str)) // 9
28 |
29 | // We can stitch two strings together by +, appending the later string to the later of the
30 | // earlier string.
31 |
32 | str1 := "hello" + "word"
33 | println(str1) // helloword
34 |
35 | str2 := "my name is \t"
36 | str3 := "zs"
37 |
38 | println(str2 + str3) // my name is zs
39 |
40 | // If we want to define a multi-line string, XGo supports that too. Using the traditional
41 | // "" is not possible across lines, we can use backquotes if we want to define a multi-line
42 | // string: `
43 |
44 | const str4 = `First line
45 | Second line
46 | Third line
47 | `
48 | println(str4)
49 |
50 | // The code between the backquotes is not recognized by the editor, but only as part of
51 | // the string.
52 |
--------------------------------------------------------------------------------
/112-Rational-Numbers/rational-1.gop:
--------------------------------------------------------------------------------
1 | // XGo has various rational number types including bigint, bigrat and bigfloat.
2 | // Here are a few basic examples on bigint type.
3 |
4 | # Declaration and assignment of bigint type variables
5 |
6 | import "math/big"
7 |
8 | // The XGo language uses the keyword "var" to declare variables.
9 | var bint1 bigint
10 | var bint2 *big.Int
11 |
12 | // the integer rational variable can be assigned when declaration.
13 | // (1r<<65), the value is equal to the 65th power of 2.
14 | var bint3 bigint = 1r << 65
15 |
16 | // Notice:
17 | // The initial value of a rational variable without assignment is not 0 but ``.
18 | //
19 | // Expected results:
20 | // bint1: ``
21 | // bint2: ``
22 | // bint3: `36893488147419103232`
23 | println "bint1:", bint1
24 | println "bint2:", bint2
25 | println "bint3:", bint3
26 |
--------------------------------------------------------------------------------
/113-If/Else/if-else.gop:
--------------------------------------------------------------------------------
1 | // Branching with `if` and `else` in XGo is
2 | // straight-forward.
3 |
4 | // Here's a basic example.
5 | if 7%2 == 0 {
6 | println "7 is even"
7 | } else {
8 | println "7 is odd"
9 | }
10 |
11 | // You can have an `if` statement without an else.
12 | if 8%4 == 0 {
13 | println "8 is divisible by 4"
14 | }
15 |
16 | // A statement can precede conditionals; any variables
17 | // declared in this statement are available in all
18 | // branches.
19 | if num := 9; num < 0 {
20 | println num, "is negative"
21 | } else if num < 10 {
22 | println num, "has 1 digit"
23 | } else {
24 | println num, "has multiple digits"
25 | }
26 |
27 | // Note that you don't need parentheses around conditions
28 | // in XGo, but that the braces are required.
29 |
--------------------------------------------------------------------------------
/113-Switch/switch-1.gop:
--------------------------------------------------------------------------------
1 | // _Switch statements_ express conditionals across many
2 | // branches.
3 |
4 | import (
5 | "time"
6 | )
7 |
8 | // Here's a basic `switch`.
9 | i := 2
10 | print "Write ", i, " as "
11 | switch i {
12 | case 1:
13 | println "one"
14 | case 2:
15 | println "two"
16 | case 3:
17 | println "three"
18 | }
19 |
20 | // You can use commas to separate multiple expressions
21 | // in the same `case` statement. We use the optional
22 | // `default` case in this example as well.
23 | switch time.now().weekday() {
24 | case time.Saturday, time.Sunday:
25 | println "It's the weekend"
26 | default:
27 | println "It's a weekday"
28 | }
29 |
30 | // `switch` without an expression is an alternate way
31 | // to express if/else logic. Here we also show how the
32 | // `case` expressions can be non-constants.
33 | t := time.now()
34 | switch {
35 | case t.hour() < 12:
36 | println "It's before noon"
37 | default:
38 | println "It's after noon"
39 | }
40 |
41 | // The switch in XGo defaults to a break at the end of each case.
42 | // Use fallthrough to enforce the code for the subsequent cases.
43 |
44 | // `switch` with `fallthrough`:
45 | score := 80
46 | switch {
47 | case score < 50:
48 | printf "%d < 50\n", score
49 | fallthrough
50 | case score < 100:
51 | printf "%d < 100\n", score
52 | fallthrough
53 | case score < 200:
54 | printf "%d < 200\n", score
55 | }
56 |
--------------------------------------------------------------------------------
/114-For/for-0.gop:
--------------------------------------------------------------------------------
1 | # For loop
2 | // XGo has only one looping keyword: for, with several forms.
3 |
--------------------------------------------------------------------------------
/114-For/for-1.gop:
--------------------------------------------------------------------------------
1 | # 1、for/in
2 | // This is the most common form. You can use it with a slice, map, numeric range or custom iterators.
3 | //
4 | // The for value in arr/map form is used for going through elements of a slice or a map.
5 | //
6 | // If an index is required, an alternative form for index, value in arr can be used.
7 |
8 | names := ["Sam", "Peter"]
9 | for i, name in names {
10 | println i, name
11 | }
12 |
13 | m := {"one": 1, "two": 2}
14 | for key, val in m {
15 | println key, val
16 | }
17 | for key, _ in m {
18 | println key
19 | }
20 |
21 | for val in names {
22 | println val
23 | }
24 | for val in m {
25 | println val
26 | }
27 |
--------------------------------------------------------------------------------
/114-For/for-2.gop:
--------------------------------------------------------------------------------
1 | # 2、Range for
2 | // You can use range expression (start:end:step) in for loop.
3 |
4 | for i in :5 {
5 | println i
6 | }
7 |
8 | for i in 1:5 {
9 | println i
10 | }
11 |
12 | for i in 1:5:2 {
13 | println i
14 | }
15 |
--------------------------------------------------------------------------------
/114-For/for-3.gop:
--------------------------------------------------------------------------------
1 | # 3、for/in/if
2 | // All loops of for/in form can have an optional if condition.
3 |
4 | numbers := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5 | for num in numbers if num%3 == 0 {
6 | println num
7 | }
8 |
9 | for num in :10 if num%3 == 0 {
10 | println num
11 | }
12 |
--------------------------------------------------------------------------------
/114-For/for-4.gop:
--------------------------------------------------------------------------------
1 | # 4、Condition for
2 | // The condition can be omitted, resulting in an infinite loop. You can use break or return to end the loop.
3 |
4 | sum := 0
5 | i := 1
6 | for i <= 100 {
7 | sum += i
8 | i++
9 | }
10 | println sum
11 |
12 | for {
13 | if sum > 0 {
14 | break
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/114-For/for-5.gop:
--------------------------------------------------------------------------------
1 | # 5、C for
2 | // 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.
3 |
4 | for i := 0; i < 10; i += 2 {
5 | if i == 6 {
6 | continue
7 | }
8 | println i
9 | }
10 |
--------------------------------------------------------------------------------
/114-For/for-6.gop:
--------------------------------------------------------------------------------
1 | # break and continue
2 | // How do you get out of an infinite for loop without using the keyboard or
3 | // turning off your computer? That’s the job of the break statement. It exits the
4 | // loop immediately, just like the break statement in other languages. Of course,
5 | // you can use break with any for statement, not just the infinite for statement.
6 | //
7 | // XGo also includes the continue keyword, which skips over rest of the body of a
8 | // for loop and proceeds directly to the next iteration. Technically, you don’t need
9 | // a continue statement.
10 |
11 | for i := 1; i <= 100; i++ {
12 | if i%3 == 0 && i%5 == 0 {
13 | println("FizzBuzz")
14 | continue
15 | }
16 | if i%3 == 0 {
17 | println("Fizz")
18 | continue
19 | }
20 | if i%5 == 0 {
21 | println("Buzz")
22 | break
23 | }
24 | println(i)
25 | }
26 |
--------------------------------------------------------------------------------
/114-For/for-7.gop:
--------------------------------------------------------------------------------
1 | # Labeling Your “for” Statements
2 | // By default, the break and continue keywords apply to the for loop that
3 | // directly contains them. What if you have nested for loops and you want to exit
4 | // or skip over an iterator of an outer loop? Let’s look at an example. We’re going
5 | // to modify our string iterating program to stop iterating through a string as
6 | // soon as it hits a letter “l”
7 |
8 | samples := []string{"hello", "apple_π!"}
9 | outer:
10 | for _, sample := range samples {
11 | for i, r := range sample {
12 | println(i, r, string(r))
13 | if r == 'l' {
14 | continue outer
15 | }
16 | }
17 | println()
18 | }
19 |
--------------------------------------------------------------------------------
/116-Arrays/arrays1.gop:
--------------------------------------------------------------------------------
1 | // In XGo, an _array_ is a numbered sequence of elements of a
2 | // specific length.
3 |
4 | # Declaration of a one-dimensional array
5 |
6 | // Here we create an array `a` that will hold exactly
7 | // 5 `int`s. The type of elements and length are both
8 | // part of the array's type. By default an array is
9 | // zero-valued, which for `int`s means `0`s.
10 | var a [5]int
11 |
12 | println "empty:", a
13 |
14 | // We can set a value at an index using the
15 | // `array[index] = value` syntax, and get a value with
16 | // `array[index]`.
17 | a[4] = 100
18 | println "set:", a
19 | println "get:", a[4]
20 |
21 | // The builtin `len` returns the length of an array.
22 | println "len:", len(a)
23 |
24 | // Use this syntax to declare and initialize an array
25 | // in one line.
26 | b := [5]int{1, 2, 3, 4, 5}
27 | println "dcl:", b
28 |
29 | // If you don't want to write the length of the array,
30 | // you can use this method and let the compiler calculate
31 | // the length of the array itself.
32 | c := [...]int{1, 2, 3}
33 | println c
34 |
--------------------------------------------------------------------------------
/116-Arrays/arrays2.gop:
--------------------------------------------------------------------------------
1 | # Declaration of multidimensional arrays
2 |
3 | // Array types are one-dimensional, but you can
4 | // compose types to build multi-dimensional data
5 | // structures.
6 | var twoD [2][3]int
7 |
8 | for i := 0; i < 2; i++ {
9 | for j := 0; j < 3; j++ {
10 | twoD[i][j] = i + j
11 | }
12 | }
13 | println "2d: ", twoD
14 |
15 | // If you need to declare more dimensions, you can extend them yourself, such as declaring a 2*2*3 three-dimensional array
16 | var threeDimensionalArray [2][2][3]int
17 | println threeDimensionalArray
18 |
--------------------------------------------------------------------------------
/116-Arrays/arrays3.gop:
--------------------------------------------------------------------------------
1 | ## tips
2 |
3 | // If you do not declare the contents of the array in XGo, the compiler automatically initializes the array to 0; For an array of type bool, the initial value is false.
4 |
5 | // For more array value types, XGo supports strings, integers, floats, Booleans, etc. See this chapter for details:
6 |
7 | // * https://tutorial.xgo.dev/values
8 |
--------------------------------------------------------------------------------
/117-Slices/slices-01.gop:
--------------------------------------------------------------------------------
1 | # Slice literals in XGo style
2 | // 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.
3 | //
4 | // In XGo, you can get slice length directly from len method, and you can casting slice literals:
5 |
6 | f64 := []float64([1, 2, 3]) // []float64
7 | println(f64, f64.len) // get length by len method
8 |
9 | nums := [1, 2, 3]
10 | println nums // [1 2 3]
11 | println nums.len // 3, XGo support
12 | println nums[0] // 1
13 | println nums[1:3] // [2 3]
14 | println nums[:2] // [1 2]
15 | println nums[2:] // [3]
16 |
17 | nums[1] = 5
18 | println nums // [1 5 3]
19 |
--------------------------------------------------------------------------------
/117-Slices/slices-02.gop:
--------------------------------------------------------------------------------
1 | # Slice
2 | // An array has a fixed size. A slice, on the other hand, is a dynamically-sized, flexible view into the elements of an array. In practice, slices are much more common than arrays.
3 | //
4 | // The type []T is a slice with elements of type T.
5 | //
6 | // A slice is formed by specifying two indices, a low and high bound, separated by a colon: a[low : high]. This selects a half-open range which includes the first element, but excludes the last one.
7 |
--------------------------------------------------------------------------------
/117-Slices/slices-03.gop:
--------------------------------------------------------------------------------
1 | # Slices are like references to arrays
2 | // A slice does not store any data, it just describes a section of an underlying array.
3 | //
4 | // Changing the elements of a slice modifies the corresponding elements of its underlying array.
5 | //
6 | // Other slices that share the same underlying array will see those changes.
7 |
8 | names := [4]string{
9 | "John",
10 | "Paul",
11 | "George",
12 | "Ringo",
13 | }
14 | println names
15 |
16 | a := names[0:2]
17 | b := names[1:3]
18 | println a, b
19 |
20 | b[0] = "XXX"
21 | println a, b
22 | println names
23 |
--------------------------------------------------------------------------------
/117-Slices/slices-04.gop:
--------------------------------------------------------------------------------
1 | # Slice literals
2 | // A slice literal is like an array literal without the length.
3 | //
4 | // This is an array literal:
5 | // [3]bool{true, true, false}
6 | // And this creates the same array as above, then builds a slice that references it:
7 | // []bool{true, true, false}
8 | //
9 |
10 | q := []int{2, 3, 5, 7, 11, 13}
11 | println q
12 |
13 | r := []bool{true, false, true, true, false, true}
14 | println r
15 |
16 | s := []struct {
17 | i int
18 | b bool
19 | }{
20 | {2, true},
21 | {3, false},
22 | {5, true},
23 | {7, true},
24 | {11, false},
25 | {13, true},
26 | }
27 | println s
28 |
--------------------------------------------------------------------------------
/117-Slices/slices-05.gop:
--------------------------------------------------------------------------------
1 | # Slice defaults
2 | // When slicing, you may omit the high or low bounds to use their defaults instead.
3 | //
4 | // The default is zero for the low bound and the length of the slice for the high bound.
5 | //
6 | // For the array
7 | // var a [10]int
8 | // these slice expressions are equivalent:
9 | // a[0:10]
10 | // a[:10]
11 | // a[0:]
12 | // a[:]
13 | //
14 | s := []int{2, 3, 5, 7, 11, 13}
15 |
16 | s = s[1:]
17 | println s
18 |
19 | s = s[1:4]
20 | println s
21 |
22 | s = s[:2]
23 | println s
24 |
--------------------------------------------------------------------------------
/117-Slices/slices-06.gop:
--------------------------------------------------------------------------------
1 | # Slice length and capacity
2 | // A slice has both a length and a capacity.
3 | //
4 | // The length of a slice is the number of elements it contains.
5 | //
6 | // The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice.
7 | //
8 | // The length and capacity of a slice s can be obtained using the expressions len(s) and cap(s).
9 | //
10 | // You can extend a slice's length by re-slicing it, provided it has sufficient capacity. Try changing one of the slice operations in the example program to extend it beyond its capacity and see what happens.
11 |
12 | func printSlice(s []int) {
13 | printf "len=%d cap=%d %v\n", len(s), cap(s), s
14 | }
15 |
16 | s := []int{2, 3, 5, 7, 11, 13}
17 | printSlice s
18 |
19 | s = s[:0] // Slice the slice to give it zero length.
20 | printSlice s
21 |
22 | s = s[:4] // Extend its length.
23 | printSlice s
24 |
25 | s = s[2:] // Drop its first two values.
26 | printSlice s
27 |
--------------------------------------------------------------------------------
/117-Slices/slices-07.gop:
--------------------------------------------------------------------------------
1 | # Nil slices
2 | // The zero value of a slice is nil.
3 | //
4 | // A nil slice has a length and capacity of 0 and has no underlying array.
5 |
6 | var s []int
7 |
8 | println s, len(s), cap(s)
9 |
10 | if s == nil {
11 | println "nil!"
12 | }
13 |
--------------------------------------------------------------------------------
/117-Slices/slices-08.gop:
--------------------------------------------------------------------------------
1 | # Creating a slice with make
2 | // Slices can be created with the built-in make function; this is how you create dynamically-sized arrays.
3 | //
4 | // The make function allocates a zeroed array and returns a slice that refers to that array:
5 | //
6 | //
13 |
14 | func printSlice(s string, x []int) {
15 | printf "%s len=%d cap=%d %v\n",
16 | s, len(x), cap(x), x
17 | }
18 |
19 | a := make([]int, 5)
20 | printSlice "a", a
21 |
22 | b := make([]int, 0, 5)
23 | printSlice "b", b
24 |
25 | c := b[:2]
26 | printSlice "c", c
27 |
28 | d := c[2:5]
29 | printSlice "d", d
30 |
--------------------------------------------------------------------------------
/117-Slices/slices-09.gop:
--------------------------------------------------------------------------------
1 | # Slices of slices
2 | // Slices can contain any type, including other slices.
3 | // Create a tic-tac-toe board.
4 |
5 | import (
6 | "strings"
7 | )
8 |
9 | board := [][]string{
10 | []string{"_", "_", "_"},
11 | []string{"_", "_", "_"},
12 | []string{"_", "_", "_"},
13 | }
14 |
15 | board[0][0] = "X" // The players take turns.
16 | board[2][2] = "O"
17 | board[1][2] = "X"
18 | board[1][0] = "O"
19 | board[0][2] = "X"
20 |
21 | for i := 0; i < len(board); i++ {
22 | printf "%s\n", strings.join(board[i], " ")
23 | }
24 |
--------------------------------------------------------------------------------
/117-Slices/slices-10.gop:
--------------------------------------------------------------------------------
1 | # Appending to a slice
2 | // It is common to append new elements to a slice, and so Go provides a built-in append function. The documentation of the built-in package describes append.
3 | //
4 | // func append(s []T, vs ...T) []T
5 | //
6 | // The first parameter s of append is a slice of type T, and the rest are T values to append to the slice.
7 | //
8 | // The resulting value of append is a slice containing all the elements of the original slice plus the provided values.
9 | //
10 | // If the backing array of s is too small to fit all the given values a bigger array will be allocated. The returned slice will point to the newly allocated array.
11 |
12 | func printSlice(s []int) {
13 | printf "len = %d cap = %d %v\n", len(s), cap(s), s
14 | }
15 |
16 | var s []int
17 |
18 | printSlice s
19 |
20 | s = append(s, 0) // append works on nil slices.
21 | printSlice s
22 |
23 | s = append(s, 1) // The slice grows as needed.
24 | printSlice s
25 |
26 | s = append(s, 2, 3, 4) // We can add more than one element at a time.
27 | printSlice s
28 |
--------------------------------------------------------------------------------
/117-Slices/slices-11.gop:
--------------------------------------------------------------------------------
1 | # Range
2 | // The range form of the for loop iterates over a slice or map.
3 | //
4 | // When ranging over a slice, two values are returned for each iteration. The first is the index, and the second is a copy of the element at that index.
5 |
6 | var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
7 |
8 | for i, v := range pow {
9 | printf "2**%d = %d\n", i, v
10 | }
11 |
--------------------------------------------------------------------------------
/117-Slices/slices-12.gop:
--------------------------------------------------------------------------------
1 | # Range continued
2 | //
3 | //
4 | // You can skip the index or value by assigning to _.
5 | // for i, _ := range pow
6 | // for _, value := range pow
7 | // If you only want the index, you can omit the second variable.
8 | // for i := range pow
9 | //
10 |
11 | pow := make([]int, 10)
12 | for i := range pow {
13 | pow[i] = 1 << uint(i) // == 2**i
14 | }
15 | for _, value := range pow {
16 | printf "%d\n", value
17 | }
18 |
--------------------------------------------------------------------------------
/118-Maps/map-0.gop:
--------------------------------------------------------------------------------
1 | // _Maps_ are XGo's built-in [associative data type](http://en.wikipedia.org/wiki/Associative_array)
2 | // (sometimes called _hashes_ or _dicts_ in other languages).
3 |
4 | # Map foundations
5 |
6 | // Use `make(map[key-type]val-type)` to create an empty map.
7 | m := make(map[string]int)
8 |
9 | // Set key/value pairs using typical `name[key] = val` syntax.
10 | m["k1"] = 7
11 | m["k2"] = 13
12 |
13 | // Printing a map with e.g. `println` will show all of
14 | // its key/value pairs.
15 | println "map:", m
16 |
17 | // Get a value for a key with `name[key]`.
18 | v1 := m["k1"]
19 | println "v1: ", v1
20 |
21 | // The builtin `len` returns the number of key/value
22 | // pairs when called on a map.
23 | println "len:", len(m)
24 |
25 | // The builtin `delete` removes key/value pairs from
26 | // a map.
27 | delete m, "k2"
28 | println "map:", m
29 |
30 | // The optional second return value when getting a
31 | // value from a map indicates if the key exists
32 | // in the map. This can be used to disambiguate
33 | // between missing keys and keys with zero values
34 | // like `0` or `""`. Here we didn't need the value
35 | // itself, so we ignored it with the _blank identifier_
36 | // `_`.
37 | _, exists := m["k2"]
38 | println "exists:", exists
39 |
--------------------------------------------------------------------------------
/118-Maps/map-1.gop:
--------------------------------------------------------------------------------
1 | # Map literals in XGo style
2 |
3 | // map[string]int
4 | println {"Hello": 1, "xsw": 3}
5 |
6 | // map[string]float64
7 | println {"Hello": 1, "xsw": 3.4}
8 |
9 | // map[string]interface{}
10 | println {"Hello": 1, "xsw": "XGo"}
11 |
12 | // map[int]interface{}
13 | println {1: 1.4, 3: "XGo"}
14 |
15 | // type of empty map is map[string]interface{}
16 | println {}
17 |
--------------------------------------------------------------------------------
/118-Maps/map-2.gop:
--------------------------------------------------------------------------------
1 | # Map literals in Go style
2 |
3 | // You can also declare and initialize a new map in
4 | // the same line with the following syntax.
5 |
6 | // {"Hello": 1, "xsw": 3}
7 | println map[string]int{"Hello": 1, "xsw": 3}
8 |
9 | // {"Hello": 1, "xsw": 3.4}
10 | println map[string]float64{"Hello": 1, "xsw": 3.4}
11 |
12 | // {"Hello": 1, "xsw": "XGo"}
13 | println map[string]interface{}{"Hello": 1, "xsw": "XGo"}
14 |
15 | // {1: 1.4, 3: "XGo"}
16 | println map[int]interface{}{1: 1.4, 3: "XGo"}
17 |
18 | // {}
19 | println map[string]interface{}{}
20 |
--------------------------------------------------------------------------------
/119-Structs/struct-1.gop:
--------------------------------------------------------------------------------
1 | // Maps are a convenient way to store some kinds of data, but they have
2 | // limitations. They don’t define an API since there’s no way to constrain a map to
3 | // only allow certain keys. All of the values in a map must also be of the same type.
4 | // For these reasons, maps are not an ideal way to pass data from function to
5 | // function. When you have related data that you want to group together, you
6 | // should define a struct.
7 | //
8 | // A struct type is defined with the keyword type, the name of the struct type, the
9 | // keyword struct, and a pair of braces ({}). Within the braces, you list the
10 | // fields in the struct. Just like we put the variable name first and the variable type
11 | // second in a var declaration, we put the struct field name first and the struct field
12 | // type second.
13 | //
14 | // Also note that unlike map literals, there are no commas separating
15 | // the fields in a struct declaration. You can define a struct type inside or outside of
16 | // a function. A struct type that’s defined within a function can only be used within
17 | // the function. Technically, you can scope a struct definition to any block level.
18 | //
19 | // Once a struct type is declared, we can define variables of that type.
20 |
21 | type person struct {
22 | name string
23 | age int
24 | pet string
25 | }
26 |
27 | var fred person
28 |
29 | println(fred)
30 |
31 | // Here we are using a var declaration. Since no value is assigned to fred, it gets
32 | // the zero value for the person struct type. A zero value struct has every field set
33 | // to the field’s zero value.
34 |
--------------------------------------------------------------------------------
/119-Structs/struct-2.gop:
--------------------------------------------------------------------------------
1 | # There are two different styles for a non-empty struct literal.
2 | // A struct literal can be specified as a comma-separated list of values for the fields
3 | // inside of braces:
4 |
5 | type person struct {
6 | name string
7 | age int
8 | pet string
9 | }
10 |
11 | tsb := person{}
12 | println(tsb)
13 |
14 | julia := person{
15 | "Julia",
16 | 40,
17 | "cat",
18 | }
19 | println(julia)
20 |
21 | // When using this struct literal format, a value for every field in the struct must be
22 | // specified, and the values are assigned to the fields in the order they were
23 | // declared in the struct definition.
24 |
--------------------------------------------------------------------------------
/119-Structs/struct-3.gop:
--------------------------------------------------------------------------------
1 | //
2 | // The second struct literal style looks like the map literal style:
3 |
4 | type person struct {
5 | name string
6 | age int
7 | pet string
8 | }
9 |
10 | beth := person{
11 | age: 30,
12 | name: "Beth",
13 | }
14 | println(beth)
15 |
16 | // You use the names of the fields in the struct to specify the values. When you use
17 | // this style, you can leave out keys and specify the fields in any order. Any field
18 | // not specified is set to its zero value. You cannot mix the two struct literal styles;
19 | // either all of the fields are specified with keys, or none of them are. For small
20 | // structs where all fields are always specified, the simpler struct literal style is
21 | // fine. In other cases, use the key names. It’s more verbose, but it makes clear
22 | // what value is being assigned to what field without having to reference the struct
23 | // definition. It’s also more maintainable. If you initialize a struct without using the
24 | // field names and a future version of the struct adds additional fields, your code
25 | // will no longer compile.
26 |
--------------------------------------------------------------------------------
/119-Structs/struct-4.gop:
--------------------------------------------------------------------------------
1 | //
2 | // A field in a struct is accessed with dotted notation:
3 |
4 | type person struct {
5 | name string
6 | age int
7 | pet string
8 | }
9 |
10 | bob := person{
11 | age: 30,
12 | name: "Beth",
13 | }
14 | bob.name = "Bob"
15 | println(bob.name)
16 |
17 | // Just like we use brackets for both reading and writing to a map, we use dotted
18 | // notation for reading and writing struct fields.
19 |
--------------------------------------------------------------------------------
/119-Structs/struct-5.gop:
--------------------------------------------------------------------------------
1 | # Anonymous Structs
2 | // You can also declare that a variable implements a struct type without first giving
3 | // the struct type a name. This is called an anonymous struct.
4 |
5 | var person struct {
6 | name string
7 | age int
8 | pet string
9 | }
10 |
11 | person.name = "bob"
12 | person.age = 50
13 | person.pet = "dog"
14 | println(person)
15 |
16 | pet := struct {
17 | name string
18 | kind string
19 | }{
20 | name: "Fido",
21 | kind: "dog",
22 | }
23 | println(pet)
24 |
25 | // In this example, the types of the variables person and pet are anonymous
26 | // structs. You assign (and read) fields in an anonymous struct just like you do for a
27 | // named struct type. Just like you can initialize an instance of a named struct with
28 | // a struct literal, you can do the same for an anonymous struct as well.
29 | // You might wonder when it’s useful to have a data type that’s only associated
30 | // with a single instance. There are two common situations where anonymous
31 | // structs are handy. The first is when you translate external data into a struct or a
32 | // struct into external data (like JSON or protocol buffers). This is called
33 | // marshaling and unmarshaling data.
34 | //
35 | // Writing tests is another place where anonymous structs pop up.
36 |
--------------------------------------------------------------------------------
/119-Structs/struct-6.gop:
--------------------------------------------------------------------------------
1 | # Comparing and Converting Structs
2 | // Whether or not a struct is comparable depends on the struct’s fields. Structs that
3 | // are entirely composed out of comparable types are comparable; those with slice
4 | // or map fields are not (as we will see in later chapters, function and channel fields
5 | // also prevent a struct from being comparable).
6 | //
7 | // Unlike Python or Ruby, there’s no magic method that can be overridden to
8 | // redefine equality and make == and != work for incomparable structs. You can,
9 | // of course, write your own function that you use to compare structs.
10 | //
11 | // Just like XGo doesn’t allow comparisons between variables of different primitive
12 | // types, XGo doesn’t allow comparisons between variables that represent structs of
13 | // different types. XGo does allow you to perform a type conversion from one struct
14 | // type to another if the fields of both structs have the same names, order, and
15 | // types. Let’s see what this means. Given this struct:
16 |
17 | type firstPerson struct {
18 | name string
19 | age int
20 | }
21 |
22 | type secondPerson struct {
23 | name string
24 | age int
25 | }
26 |
27 | type thirdPerson struct {
28 | age int
29 | name string
30 | }
31 |
32 | first := firstPerson{name: "Bob", age: 22}
33 | second := secondPerson{name: "Joe", age: 23}
34 | third := thirdPerson{name: "Sars", age: 24}
35 | first = firstPerson(second)
36 | println first
37 | first = firstPerson(third)
38 | println first
39 |
40 | // We can use a type conversion to convert an instance of secondPerson to
41 | // firstPerson, but we can’t convert an instance of thirdPerson to firstPerson, because the
42 | // fields are in a different order.
43 | // But we can’t use == to compare an instance of firstPerson and an instance of secondPerson or thirdPerson,
44 | // because they are different types.
45 |
--------------------------------------------------------------------------------
/119-Structs/struct-7.gop:
--------------------------------------------------------------------------------
1 | // Anonymous structs add a small twist to this: if two struct variables are being
2 | // compared and at least one of them has a type that’s an anonymous struct, you
3 | // can compare them without a type conversion if the fields of both structs have the
4 | // same names, order, and types. You can also assign between named and
5 | // anonymous struct types if the fields of both structs have the same names, order,
6 | // and types.
7 |
8 | type firstPerson struct {
9 | name string
10 | age int
11 | }
12 |
13 | f := firstPerson{
14 | name: "Bob",
15 | age: 50,
16 | }
17 | var g struct {
18 | name string
19 | age int
20 | }
21 |
22 | g = f
23 | println f == g
24 |
--------------------------------------------------------------------------------
/120-Pointers/pointer-1.gop:
--------------------------------------------------------------------------------
1 | // A pointer is a variable whose value is a memory address. Pointers are defined using an ampersand (the & character), known as the address operator, followed by
2 | // the name of a variable
3 | //
4 | // A pointer’s type is fixed, which means that when you create a pointer
5 | // to an int, for example, you change the value that it points to, but you can’t use it to point to the memory
6 | // address used to store a different type, such as a float64. This restriction is important, pointers are
7 | // not just memory addresses but, rather, memory addresses that may store a specific type of value.
8 | //
9 | // The type of a pointer is based on the type of the variable from which it is created, prefixed with an
10 | // asterisk (the * character). The type of variable named second is *int, because it was created by applying
11 | // the address operator to the first variable, whose value is int. When you see the type *int, you know it is a
12 | // variable whose value is a memory address that stores an int variable.
13 | //
14 |
15 | first := 100
16 | var second *int = &first
17 | first++
18 | println("First:", first)
19 | println("Second:", second)
20 |
--------------------------------------------------------------------------------
/120-Pointers/pointer-2.gop:
--------------------------------------------------------------------------------
1 | # Following a Pointer
2 | // The phrase following a pointer means reading the value at the memory address that the pointer refers
3 | // to, and it is done using an asterisk (the * character). The asterisk tells XGo to follow the pointer and get the value at the memory location. This is known as dereferencing the pointer.
4 |
5 | first := 100
6 | second := &first
7 | first++
8 | *second++
9 | var myNewPointer *int
10 | myNewPointer = second
11 | *myNewPointer++
12 | println("First:", first)
13 | println("Second:", *second)
14 |
15 | // The first new statement defines a new variable, which I have done with the var keyword to emphasize
16 | // that the variable type is *int, meaning a pointer to an int value. The next statement assigns the value of
17 | // the second variable to the new variable, meaning that the values of both second and myNewPointer are the
18 | // memory location of the first value. Following either pointer accesses the same memory location, which
19 | // means incrementing myNewPointer affects the value obtained by following the second pointer.
20 | //
21 | // A common misconception is that the first and second variables have the same value, but that’s not
22 | // what is happening. There are two values. There is an int value that can be accessed using the variable
23 | // named first. There is also an *int value that stores the memory location of the first value. The *int value
24 | // can be followed, which will access the stored int value. But, because the *int value is, well, a value, it can be
25 | // used in its own right, which means that it can be assigned to other variables, used as an argument to invoke a
26 | // function, and so on.
27 |
--------------------------------------------------------------------------------
/120-Pointers/pointer-3.gop:
--------------------------------------------------------------------------------
1 | # Understanding Pointer Zero Values
2 | // Pointers that are defined but not assigned a value have the zero-value nil.
3 | //
4 | // The pointer second is defined but not initialized with a value and is written out using the println
5 | // function. The address operator is used to create a pointer to the first variable, and the value of second is
6 | // written out again.
7 | //
8 | // A runtime error will occur if you follow a pointer that has not been assigned a value
9 |
10 | first := 100
11 | var second *int
12 | println(second)
13 | second = &first
14 | println(second)
15 |
--------------------------------------------------------------------------------
/120-Pointers/pointer-4.gop:
--------------------------------------------------------------------------------
1 | # Pointing at Pointers
2 | // Given that pointers store memory locations, it is possible to create a pointer whose value is the memory
3 | // address of another pointer.
4 |
5 | first := 100
6 | second := &first
7 | third := &second
8 | println(first)
9 | println(*second)
10 | println(**third)
11 |
12 | // The syntax for following chains of pointers can be awkward. In this case, two asterisks are required.
13 | // The first asterisk follows the pointer to the memory location to get the value stored by the variable named
14 | // second, which is an *int value. The second asterisk follows the pointer named second, which gives access
15 | // to the memory location of the value stored by the first variable. This isn’t something you will need to do
16 | // in most projects, but it does provide a nice confirmation of how pointers work and how you can follow the
17 | // chain to get to the data value.
18 |
--------------------------------------------------------------------------------
/121-For-Each/for-each-1.gop:
--------------------------------------------------------------------------------
1 | // In XGo there is no foreach loop instead, the for loop can be used as “foreach“. There is a keyword range, you can combine for and range together and have the choice of using the key or value within the loop.
2 |
--------------------------------------------------------------------------------
/121-For-Each/for-each-2.gop:
--------------------------------------------------------------------------------
1 | # The for-range Statement
2 | // What makes a for-range loop interesting is that you get two loop variables.
3 | // The first variable is the position in the data structure being iterated, while the
4 | // second is value at that position. The idiomatic names for the two loop variables
5 | // depend on what is being looped over. When looping over an array, slice, or
6 | // string, an i for index is commonly used. When iterating through a map, k (for
7 | // key) is used instead.
8 | //
9 | // The second variable is frequently called v for value, but is sometimes given a
10 | // name based on the type of the values being iterated.
11 | //
12 | // If you don’t need to access the key, use an underscore (_) as the variable’s name. This tells XGo to ignore the
13 | // value.
14 |
15 | evenVals := []int{2, 4, 6, 8, 10, 12}
16 | for _, v := range evenVals {
17 | println(v)
18 | }
19 |
--------------------------------------------------------------------------------
/121-For-Each/for-each-3.gop:
--------------------------------------------------------------------------------
1 | # Iterating Over Maps
2 | // There’s something interesting about how a for-range loop iterates over a
3 | // map.
4 |
5 | m := map[string]int{
6 | "a": 1,
7 | "c": 3,
8 | "b": 2,
9 | }
10 | for i := 0; i < 3; i++ {
11 | println("Loop", i)
12 | for k, v := range m {
13 | println(k, v)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/121-For-Range/range.gop:
--------------------------------------------------------------------------------
1 | // _range_ iterates over elements in a variety of data
2 | // structures. Let's see how to use `range` with some
3 | // of the data structures we've already learned.
4 |
5 | // Here we use `range` to sum the numbers in a slice.
6 | // Arrays work like this too.
7 | nums := [2, 3, 4]
8 | sum := 0
9 | for _, num := range nums {
10 | sum += num
11 | }
12 | println "sum:", sum
13 |
14 | // `range` on arrays and slices provides both the
15 | // index and value for each entry. Above we didn't
16 | // need the index, so we ignored it with the
17 | // blank identifier `_`. Sometimes we actually want
18 | // the indexes though.
19 | for i, num := range nums {
20 | if num == 3 {
21 | println "index:", i
22 | }
23 | }
24 |
25 | // `range` on map iterates over key/value pairs.
26 | kvs := {"a": "apple", "b": "banana"}
27 | for k, v := range kvs {
28 | printf "%s -> %s\n", k, v
29 | }
30 |
31 | // `range` can also iterate over just the keys of a map.
32 | for k := range kvs {
33 | println "key:", k
34 | }
35 |
36 | // `range` on strings iterates over Unicode code
37 | // points. The first value is the starting byte index
38 | // of the `rune` and the second the `rune` itself.
39 | for i, c := range "go" {
40 | println i, c
41 | }
42 |
--------------------------------------------------------------------------------
/121-List-Comprehension/listcompr.gop:
--------------------------------------------------------------------------------
1 | // Generating list of odd and even numbers.
2 | odds := [x for x in 1:10:2]
3 | evens := [x for x in 2:10:2]
4 | println "odds:", odds
5 | println "evens:", evens
6 |
7 | // Squares
8 | squares := [[x, x*x] for x in 0:10]
9 | println "squares:", squares
10 |
11 | // Iterations
12 | iterated := [[[y, [x, x*x, x+y]] for y in 0:10] for x in 0:10]
13 | println "Tracking iterations:", iterated
14 |
15 | // Split a string
16 | str := "xgo/tutorials"
17 | println [x for x in str.split("")]
18 |
19 | // Split a string on given character
20 | SplitOn := func(ch, str string) []string {
21 | return [x for x in str.split(ch)]
22 | }
23 | println SplitOn("t", "xgo/tutorials")
24 |
--------------------------------------------------------------------------------
/200-Structured programming/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/goplus/tutorial/70c515cad9e678a688116060eaa91149dc4ca512/200-Structured programming/README.md
--------------------------------------------------------------------------------
/201-Functions/funcs.gop:
--------------------------------------------------------------------------------
1 | // _Functions_ are central in XGo. We'll learn about
2 | // functions with a few different examples.
3 |
4 | // Here's a function that takes two `int`s and returns
5 | // their sum as an `int`.
6 | func plus(a int, b int) int {
7 |
8 | // XGo requires explicit returns, i.e. it won't
9 | // automatically return the value of the last
10 | // expression.
11 | return a + b
12 | }
13 |
14 | // When you have multiple consecutive parameters of
15 | // the same type, you may omit the type name for the
16 | // like-typed parameters up to the final parameter that
17 | // declares the type.
18 | func plusPlus(a, b, c int) int {
19 | return a + b + c
20 | }
21 |
22 | // Call a function just as you'd expect, with
23 | // `name(args)`.
24 | res := plus(1, 2)
25 | println "1+2 =", res
26 |
27 | res = plusPlus(1, 2, 3)
28 | println "1+2+3 =", res
29 |
--------------------------------------------------------------------------------
/202-Multiple-Return-Values/multi-rets.gop:
--------------------------------------------------------------------------------
1 | // XGo has built-in support for _multiple return values_.
2 | // This feature is used often in idiomatic XGo, for example
3 | // to return both result and error values from a function.
4 |
5 | import "fmt"
6 |
7 | // The `(int, int)` in this function signature shows that
8 | // the function returns 2 `int`s.
9 | func vals() (int, int) {
10 | return 3, 7
11 | }
12 |
13 | // Here we use the 2 different return values from the
14 | // call with _multiple assignment_.
15 | a, b := vals()
16 | println a
17 | println b
18 |
19 | // If you only want a subset of the returned values,
20 | // use the blank identifier `_`.
21 | _, c := vals()
22 | println c
23 |
--------------------------------------------------------------------------------
/203-Errors/errors.gop:
--------------------------------------------------------------------------------
1 | // In XGo it's idiomatic to communicate errors via an
2 | // explicit, separate return value. This contrasts with
3 | // the exceptions used in languages like Java and Ruby and
4 | // the overloaded single result / error value sometimes
5 | // used in C. XGo's approach makes it easy to see which
6 | // functions return errors and to handle them using the
7 | // same language constructs employed for any other,
8 | // non-error tasks.
9 |
10 | import (
11 | "errors"
12 | )
13 |
14 | // By convention, errors are the last return value and
15 | // have type `error`, a built-in interface.
16 | func f1(arg int) (int, error) {
17 | if arg == 42 {
18 |
19 | // `errors.New` constructs a basic `error` value
20 | // with the given error message.
21 | return -1, errors.new("can't work with 42")
22 |
23 | }
24 |
25 | // A `nil` value in the error position indicates that
26 | // there was no error.
27 | return arg + 3, nil
28 | }
29 |
30 | // It's possible to use custom types as `error`s by
31 | // implementing the `Error()` method on them. Here's a
32 | // variant on the example above that uses a custom type
33 | // to explicitly represent an argument error.
34 | type argError struct {
35 | arg int
36 | prob string
37 | }
38 |
39 | func (e *argError) Error() string {
40 | return sprintf("%d - %s", e.arg, e.prob)
41 | }
42 |
43 | func f2(arg int) (int, error) {
44 | if arg == 42 {
45 |
46 | // In this case we use `&argError` syntax to build
47 | // a new struct, supplying values for the two
48 | // fields `arg` and `prob`.
49 | return -1, &argError{arg, "can't work with it"}
50 | }
51 | return arg + 3, nil
52 | }
53 |
54 | // The two loops below test out each of our
55 | // error-returning functions. Note that the use of an
56 | // inline error check on the `if` line is a common
57 | // idiom in XGo code.
58 | for _, i := range []int{7, 42} {
59 | if r, e := f1(i); e != nil {
60 | println "f1 failed:", e
61 | } else {
62 | println "f1 worked:", r
63 | }
64 | }
65 | for _, i := range []int{7, 42} {
66 | if r, e := f2(i); e != nil {
67 | println "f2 failed:", e
68 | } else {
69 | println "f2 worked:", r
70 | }
71 | }
72 |
73 | // If you want to programmatically use the data in
74 | // a custom error, you'll need to get the error as an
75 | // instance of the custom error type via type
76 | // assertion.
77 | _, e := f2(42)
78 | if ae, ok := e.(*argError); ok {
79 | println ae.arg
80 | println ae.prob
81 | }
82 |
--------------------------------------------------------------------------------
/204-Function Values/func-values.gop:
--------------------------------------------------------------------------------
1 | //Functions are values too. They can be passed around just like other values.
2 | //Function values may be used as function arguments and return values.
3 |
4 | import (
5 | "math"
6 | )
7 |
8 | func compute(fn func(float64, float64) float64) float64 {
9 | return fn(3, 4)
10 | }
11 |
12 | hypot := func(x, y float64) float64 {
13 | return math.sqrt(x*x + y*y)
14 | }
15 | println hypot(5, 12)
16 |
17 | println compute(hypot)
18 | println compute(math.Pow)
19 |
--------------------------------------------------------------------------------
/205-Closures/closures.gop:
--------------------------------------------------------------------------------
1 | //XGo functions may be closures. A closure is a function value that references variables from outside its body.
2 | //The function may access and assign to the referenced variables; in this sense the function is "bound" to the variables.
3 | //For example, the adder function returns a closure. Each closure is bound to its own sum variable.
4 |
5 | func adder() func(int) int {
6 | sum := 0
7 | return func(x int) int {
8 | sum += x
9 | return sum
10 | }
11 | }
12 |
13 | pos, neg := adder(), adder()
14 | for i := 0; i < 10; i++ {
15 | println pos(i), neg(-2*i)
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/205-Lambda-expressions/lambda-expressions-1.gop:
--------------------------------------------------------------------------------
1 | // Lambda expressions are used when we want to define a function inline without giving it any name.
2 |
3 | // The following example shows the lambda expression of XGo style, which is more compact and easy to understand.
4 |
5 | func transform(a []float64, f func(float64) float64) []float64 {
6 | return [f(x) for x in a]
7 | }
8 |
9 | y := transform([1, 2, 3], x => x * x)
10 | println y // [1 4 9]
11 |
12 | z := transform([-3, 1, -5], x => {
13 | if x < 0 {
14 | return -x
15 | }
16 | return x
17 | })
18 | println z // [3 1 5]
19 |
--------------------------------------------------------------------------------
/205-Lambda-expressions/lambda-expressions-2.gop:
--------------------------------------------------------------------------------
1 | // If we want a lambda to be defined without being executed, we only omit its identifier. For example:
2 |
3 | func returnLambda() func(string) {
4 | return func(msg string) {
5 | println msg
6 | }
7 | }
8 |
9 | consoleLog := returnLambda()
10 | consoleLog "Hello"
11 |
--------------------------------------------------------------------------------
/205-Lambda-expressions/lambda-expressions-3.gop:
--------------------------------------------------------------------------------
1 | // If we want the lambda to be defined and executed, we omit its identifier and add an argument list in parentheses after the body’s closing curly brace. For example:
2 |
3 | func(msg string) {
4 | println msg
5 | }("Hello")
6 |
--------------------------------------------------------------------------------
/206-Recursion/recursion.gop:
--------------------------------------------------------------------------------
1 | // The XGo programming language supports recursion. That is, it allows a function to call itself. But while using recursion, programmers need to be careful to define an exit condition from the function, otherwise it will go on to become an infinite loop.
2 | // Recursive functions are very useful to solve many mathematical problems such as calculating factorial of a number, generating a Fibonacci series, etc.
3 |
4 | // This example calculates the factorial of a given number using a recursive function
5 | func factorial(i int) int {
6 | if i <= 1 {
7 | return 1
8 | }
9 | return i * factorial(i-1)
10 | }
11 |
12 | // This Example shows how to generate a Fibonacci series of a given number using a recursive function
13 | func fibonaci(i int) (ret int) {
14 | if i == 0 {
15 | return 0
16 | }
17 | if i == 1 {
18 | return 1
19 | }
20 | return fibonaci(i-1) + fibonaci(i-2)
21 | }
22 |
23 | println "Factorial of 15 is", factorial(15)
24 | for i := 0; i < 10; i++ {
25 | println fibonaci(i)
26 | }
27 |
--------------------------------------------------------------------------------
/207-Variadic-Parameters/variadic.gop:
--------------------------------------------------------------------------------
1 | import "strings"
2 |
3 | func joinstr(elements ...string) string {
4 | return strings.join(elements, "-")
5 | }
6 |
7 | elements := []string{"geeks", "FOR", "geeks"}
8 | println joinstr("one")
9 | println joinstr("one", "two")
10 | println joinstr("one", "two", "three")
11 | println joinstr(elements...)
12 |
13 | //joinstr function that is called with the varying number of arguments is known as variadic function.
14 | //In the declaration of the joinstr function, the type of the last parameter is preceded by an ellipsis, i.e, (…).
15 | //It indicates that the function can be called at any number of parameters of string.
16 | //The call joinstr(elements...), if without that ..., it wouldn't compile because the types would be wrong; elements is not of type string.
17 |
--------------------------------------------------------------------------------
/208-Defer/defer-1.gop:
--------------------------------------------------------------------------------
1 | // A defer statement defers the execution of a function until the surrounding function returns.
2 | // The deferred call's arguments are evaluated immediately,
3 | // but the function call is not executed until the surrounding function returns.
4 | // Defer is commonly used to simplify functions that perform various clean-up actions.
5 |
6 | import (
7 | "io"
8 | "os"
9 | )
10 |
11 | func CopyFile(dstName, srcName string) (written int64, err error) {
12 | src, err := os.open(srcName)
13 | if err != nil {
14 | return
15 | }
16 | defer src.close()
17 |
18 | dst, err := os.create(dstName)
19 | if err != nil {
20 | return
21 | }
22 | defer dst.close()
23 |
24 | return io.copy(dst, src)
25 | }
26 |
27 | // Defer statements allow us to think about closing each file right after opening it, guaranteeing that, regardless of the number of return statements in the function, the files will be closed.
28 |
--------------------------------------------------------------------------------
/208-Defer/defer-2.gop:
--------------------------------------------------------------------------------
1 | # Stacking defers
2 |
3 | // Deferred function calls are pushed onto a stack. When a function returns, its deferred calls are executed in last-in-first-out order.
4 |
5 | println("counting")
6 | for i := 0; i < 10; i++ {
7 | defer println(i)
8 | }
9 | println("done")
10 |
--------------------------------------------------------------------------------
/209-Exceptions/exceptions-1.gop:
--------------------------------------------------------------------------------
1 | # Panic
2 | // Panic is a built-in function that stops the normal execution flow. When you call panic in your code, it means you’ve decided that your caller can’t solve the problem. Therefore, you should use panic only in rare cases where it’s not safe for your code or anyone integrating your code to continue at that point.
3 | // The code sample below demonstrates how panic works:
4 |
5 | import (
6 | "errors"
7 | )
8 |
9 | func A() {
10 | defer println("Then we can't save the earth!")
11 | B()
12 | }
13 |
14 | func B() {
15 | defer println("And if it keeps getting hotter...")
16 | C()
17 | }
18 |
19 | func C() {
20 | defer println("Turn on the air conditioner...")
21 | Break()
22 | }
23 |
24 | func Break() {
25 | defer println("If it's more than 30 degrees...")
26 | panic(errors.New("Global Warming!!!"))
27 | println("Goodbye!")
28 | }
29 |
30 | A()
31 |
32 | // As shown above, when panic is used and not handled, the execution flow stops, all deferred functions are executed in reverse order, and stack traces are printed.
33 |
--------------------------------------------------------------------------------
/209-Exceptions/exceptions-2.gop:
--------------------------------------------------------------------------------
1 | # Recover
2 |
3 | // To report an error as a return value, you have to call the recover function in the same goroutine as where the panic function is called, retrieve an error struct from the recover function, and pass it to a variable:
4 |
5 | import (
6 | "errors"
7 | )
8 |
9 | func saveEarth() (err error) {
10 |
11 | defer func() {
12 | if r := recover(); r != nil {
13 | err = r.(error)
14 | }
15 | }()
16 | TooLate()
17 | return
18 | }
19 |
20 | func TooLate() {
21 | A()
22 | panic(errors.New("Then there's nothing we can do"))
23 | }
24 |
25 | func A() {
26 | defer println("If it's more than 100 degrees...")
27 | }
28 |
29 | err := saveEarth()
30 | println(err)
31 |
32 | // Every deferred function will be executed after a function call but before a return statement. Therefore, you can set a returned variable before a return statement is executed.
33 |
--------------------------------------------------------------------------------
/210-Methods/methods-1.gop:
--------------------------------------------------------------------------------
1 | // XGo does not have classes. However, you can define methods on types.
2 | // A method is a function with a special receiver argument.
3 | // The receiver appears in its own argument list between the func keyword and the method name.
4 |
5 | import (
6 | "math"
7 | )
8 |
9 | type Vertex struct {
10 | X, Y float64
11 | }
12 |
13 | func (v Vertex) Abs() float64 {
14 | return math.Sqrt(v.X*v.X + v.Y*v.Y)
15 | }
16 |
17 | v := Vertex{3, 4}
18 | println(v.Abs())
19 |
--------------------------------------------------------------------------------
/210-Methods/methods-2.gop:
--------------------------------------------------------------------------------
1 | # Declare a method on non-struct types
2 |
3 | import (
4 | "math"
5 | )
6 |
7 | type MyFloat float64
8 |
9 | func (f MyFloat) Abs() float64 {
10 | if f < 0 {
11 | return float64(-f)
12 | }
13 | return float64(f)
14 | }
15 |
16 | f := MyFloat(-math.Sqrt2)
17 | println(f.Abs())
18 |
19 | // In this example we see a numeric type MyFloat with an Abs method.
20 | // You can only declare a method with a receiver whose type is defined in the same package as the method. You cannot declare a method with a receiver whose type is defined in another package (which includes the built-in types such as int).
21 |
--------------------------------------------------------------------------------
/211-Methods-with-a-Pointer-Receiver/ptr-methods-1.gop:
--------------------------------------------------------------------------------
1 | # Pointer receivers
2 |
3 | // You can declare methods with pointer receivers.
4 |
5 | // This means the receiver type has the literal syntax *T for some type T. (Also, T cannot itself be a pointer such as *int.)
6 |
7 | import (
8 | "math"
9 | )
10 |
11 | type Vertex struct {
12 | X, Y float64
13 | }
14 |
15 | func (v Vertex) Abs() float64 {
16 | return math.sqrt(v.X*v.X + v.Y*v.Y)
17 | }
18 |
19 | func (v *Vertex) Scale(f float64) {
20 | v.X = v.X * f
21 | v.Y = v.Y * f
22 | }
23 |
24 | v := Vertex{3, 4}
25 | v.scale 10
26 | println v.abs()
27 |
28 | // For example, the Scale method here is defined on *Vertex.
29 |
30 | // Methods with pointer receivers can modify the value to which the receiver points (as Scale does here). Since methods often need to modify their receiver, pointer receivers are more common than value receivers.
31 |
32 | // With a value receiver, the Scale method operates on a copy of the original Vertex value. (This is the same behavior as for any other function argument.) The Scale method must have a pointer receiver to change the Vertex value.
33 |
--------------------------------------------------------------------------------
/212-Composing-Types-by-Struct-Embedding/struct-emb-1.gop:
--------------------------------------------------------------------------------
1 | // XGo does not provide the typical, type-driven notion of subclassing, but it does have the ability to “borrow” pieces of an implementation by embedding types within a struct or interface.
2 | // Only interfaces can be embedded within interfaces.
3 |
4 | // The Sample has two struct types, Hello struct and Goodbye struct, each of which implements the Talk interface. And HelloGoodbye struct also implements Talk interface, which it does by combining Hello struct and Goodbye struct into one struct using embedding: it lists the types within the struct but does not give them field names.
5 |
6 | type Talk interface {
7 | Say()
8 | }
9 |
10 | type Hello struct {
11 | name string
12 | }
13 |
14 | func (hello *Hello) Say() {
15 | println "Hello ", hello.name
16 | }
17 |
18 | func (hello *Hello) Sleep() {
19 | println "Hello ", hello.name, "go to bed!"
20 | }
21 |
22 | type Goodbye struct {
23 | name string
24 | }
25 |
26 | func (goodbye *Goodbye) Say() {
27 | println "Goodbye ", goodbye.name
28 | }
29 |
30 | type Forward struct {
31 | }
32 |
33 | func (forward *Forward) Say() {
34 | forward.SayForward()
35 | }
36 |
37 | func (forward *Forward) SayForward() {
38 | println "You must forward method!"
39 | }
40 |
41 | type HelloGoodbye struct {
42 | *Hello
43 | *Goodbye
44 | forward *Forward
45 | }
46 |
47 | func (hg *HelloGoodbye) Say() {
48 | hg.Hello.Say()
49 | hg.Goodbye.Say()
50 | hg.forward.Say()
51 | }
52 |
53 | helloGoodbye := HelloGoodbye{
54 | &Hello{"tsingbx"},
55 | &Goodbye{"tsingbx"},
56 | &Forward{},
57 | }
58 | helloGoodbye.Say()
59 | println()
60 | helloGoodbye.Sleep()
61 | println()
62 | helloGoodbye.forward.SayForward()
63 |
64 | // The embedded elements are pointers to structs and of course must be initialized to point to valid structs before they can be used.
65 |
66 | // The HelloGoodbye struct also have forward member written as forward *Forward, but then to promote the methods of the forward and to satisfy the Talk interface, we would also need to provide forwarding methods, like this: hg.forward.Say(). By embedding the structs directly, we avoid this bookkeeping. The methods of embedded types come along for free, which means that HelloGoodbye struct also has the methods of Hello struct and Goodbye struct.
67 |
68 | // There's an important way in which embedding differs from subclassing. When we embed a type, the methods of that type become methods of the outer type, but when they are invoked the receiver of the method is the inner type, not the outer one. In our example, when the Sleep method of a HelloGoodbye is invoked, it has exactly the same effect as the forwarding method helloGoodbye.Hello.Sleep(); the receiver is the helloGoodbye.Hello, not the helloGoodbye itself.
69 |
--------------------------------------------------------------------------------
/212-Composing-Types-by-Struct-Embedding/struct-emb-2.gop:
--------------------------------------------------------------------------------
1 | # Embedding can also be a simple convenience.
2 | // This example shows an embedded field alongside a regular, named field.
3 |
4 | import "log"
5 | import "fmt"
6 | import "os"
7 |
8 | type Job struct {
9 | Command string
10 | *log.Logger
11 | }
12 |
13 | func (job *Job) Printf(format string, args ...interface{}) {
14 | job.Logger.Printf("%q: %s", job.Command, fmt.Sprintf(format, args...))
15 | }
16 |
17 | func NewJob(command string, logger *log.Logger) *Job {
18 | return &Job{command, logger}
19 | }
20 |
21 | job := &Job{"cmd", log.New(os.Stderr, "Job: ", log.Ldate)}
22 | job.Println("starting now...")
23 | job.Printf("format string %d", 1)
24 |
25 | // The Job type now has the Print, Printf, Println and other methods of *log.Logger. We could have given the Logger a field name, of course, but it's not necessary to do so. And now, once initialized, we can log to the Job: job.Println("starting now...")
26 |
27 | // The Logger is a regular field of the Job struct, so we can initialize it in the usual way inside the constructor for Job, like function NewJob do, or with a composite literal, job := &Job{command, log.New(os.Stderr, "Job: ", log.Ldate)}
28 |
29 | // If we need to refer to an embedded field directly, the type name of the field, ignoring the package qualifier, serves as a field name, as it did in the Printf method of our Job struct. Here, if we needed to access the *log.Logger of a Job variable job, we would write job.Logger, which would be useful if we wanted to refine the methods of Logger.
30 |
31 | // Embedding types introduces the problem of name conflicts but the rules to resolve them are simple. First, a field or method X hides any other item X in a more deeply nested part of the type. If log.Logger contained a field or method called Command, the Command field of Job would dominate it.
32 |
33 | // Second, if the same name appears at the same nesting level, it is usually an error; it would be erroneous to embed log.Logger if the Job struct contained another field or method called Logger. However, if the duplicate name is never mentioned in the program outside the type definition, it is OK. This qualification provides some protection against changes made to types embedded from outside; there is no problem if a field is added that conflicts with another field in another subtype if neither field is ever used.
34 |
--------------------------------------------------------------------------------
/213-Method-Values-and-Expressions/method-values-1.gop:
--------------------------------------------------------------------------------
1 | # Method value
2 |
3 | // Method values allow you to bind a method to a specific object, and then call the method as an ordinary function with the object implied, in a kind of closure. For example:
4 |
5 | type Hello struct {
6 | }
7 |
8 | func (hello *Hello) Say() {
9 | println "Hello"
10 | }
11 |
12 | hello := &Hello{}
13 |
14 | hello.Say
15 |
16 | printf("%T", hello.Say)
17 |
18 | // The expression hello.Say creates a method value, binding the Say function to the specific variable hello, giving it a type of func(). So function values can be passed as function parameters
19 |
--------------------------------------------------------------------------------
/213-Method-Values-and-Expressions/method-values-2.gop:
--------------------------------------------------------------------------------
1 | # Method expressions
2 |
3 | // Method expressions let you transform a method into a function with the receiver as its first argument. For example:
4 |
5 | type Point struct {
6 | x float64
7 | y float64
8 | }
9 |
10 | func (p Point) Add(another Point) Point {
11 | return Point{p.x + another.x, p.y + another.y}
12 | }
13 |
14 | func (p Point) String() string {
15 | return sprintf("x: %.1f, y: %.1f", p.x, p.y)
16 | }
17 |
18 | p := Point{20, 10}
19 | another := Point{30, 40}
20 |
21 | println p.Add(another)
22 | println Point.Add(p, another)
23 |
24 | // So if you defined a Point struct and a method func (p Point) Add(another Point), you could write Point.Add to get a func(p Point, another Point).
25 |
--------------------------------------------------------------------------------
/213-Method-Values-and-Expressions/method-values-3.gop:
--------------------------------------------------------------------------------
1 | // But if the method has a pointer receiver, you need to write (*Type).Method in your method expression. For example:
2 |
3 | type Point struct {
4 | x float64
5 | y float64
6 | }
7 |
8 | func (p *Point) Add(another Point) {
9 | p.x += another.x
10 | p.y += another.y
11 | }
12 |
13 | func (p *Point) String() string {
14 | return sprintf("x: %.1f, y: %.1f", p.x, p.y)
15 | }
16 |
17 | p := &Point{20, 10}
18 | another := Point{30, 40}
19 |
20 | p.Add(another)
21 | (*Point).Add(p, another)
22 |
23 | println p
24 |
--------------------------------------------------------------------------------
/214-Encapsulation/encap-1.gop:
--------------------------------------------------------------------------------
1 | // Golang provides encapsulation at the package level. Go doesn’t have any public, private or protected keyword. The only mechanism to control the visibility is using the capitalized and non-capitalized formats
2 |
3 | // Capitalized Identifiers are exported. The capital letter indicates that this is an exported identifier.
4 | // Non-capitalized identifiers are not exported. The lowercase indicates that the identifier is not exported and will only be accessed from within the same package.
5 |
6 | // There are five kinds of identifier which can be exported or non-exported.
7 |
--------------------------------------------------------------------------------
/214-Encapsulation/encap-2.gop:
--------------------------------------------------------------------------------
1 | # 1. Export Struct
2 |
3 | package company
4 |
5 | type PublicCompany struct {
6 | Name string
7 | address string
8 | }
9 |
10 | type privateCompany struct {
11 | name string
12 | address string
13 | }
14 |
15 | // Here, in company package, the struct PublicCompany is exported, struct privateCompany is non-exported.
16 |
--------------------------------------------------------------------------------
/214-Encapsulation/encap-3.gop:
--------------------------------------------------------------------------------
1 | # 2. Export structure method
2 |
3 | package person
4 |
5 | type Person struct {
6 | age int
7 | name string
8 | }
9 |
10 | func (person *Person) GetAge() int {
11 | return person.age
12 | }
13 |
14 | func (person *Person) getName() string {
15 | return person.name
16 | }
17 |
18 | // The Person struct's method GetAge() is exported and getName is not exported.
19 |
--------------------------------------------------------------------------------
/214-Encapsulation/encap-4.gop:
--------------------------------------------------------------------------------
1 | # 3. Export structure field
2 |
3 | package student
4 |
5 | type Student struct {
6 | Name string
7 | age int
8 | }
9 |
10 | // The Student struct field Name is exported. The Student struct field age is not exported.
11 |
--------------------------------------------------------------------------------
/214-Encapsulation/encap-5.gop:
--------------------------------------------------------------------------------
1 | # 4. Export function
2 |
3 | package MathTools
4 |
5 | func Sum(nums ...int) int {
6 | var s int = 0
7 | for _, num := range nums {
8 | s += num
9 | }
10 | return s
11 | }
12 |
13 | func average(nums ...int) int {
14 | if len(nums) <= 0 {
15 | return 0
16 | }
17 | return Sum(nums...) / len(nums)
18 | }
19 |
20 | println Sum(1, 2, 3), average(1, 2, 3)
21 |
22 | println Sum(1, 2, 3)
23 |
24 | // The function Sum is exported, and the function average is not exported.
25 |
--------------------------------------------------------------------------------
/214-Encapsulation/encap-6.gop:
--------------------------------------------------------------------------------
1 | # 5. Export variable
2 |
3 | package vars
4 |
5 | var (
6 | PersonName = "Wang yang"
7 | personAge = 21
8 | )
9 |
10 | // The variable PersonName is exported, and the variable personAge is not exported.
11 |
--------------------------------------------------------------------------------
/215-Interfaces/interfaces-1.gop:
--------------------------------------------------------------------------------
1 | import "math"
2 |
3 | // First, we define a very simple interface on geometry, containing the two most basic
4 | // interface methods - calculating the area and calculating the perimeter - with the following code:
5 | type geometry interface {
6 | area() float64
7 | perim() float64
8 | }
9 |
10 | // Next, we define two structs, a rectangular struct and a circular Rectangular
11 | // struct, with the following code:
12 | type rect struct {
13 | width float64
14 | height float64
15 | }
16 |
17 | type circle struct {
18 | radius float64
19 | }
20 |
21 | // Then implement the interface methods defined above respectively, and the code
22 | // related to the rectangular structure is as follows:
23 | func (c circle) area() float64 {
24 | return math.Pi * c.radius * c.radius
25 | }
26 |
27 | func (c circle) perim() float64 {
28 | return 2 * math.Pi * c.radius
29 | }
30 |
--------------------------------------------------------------------------------
/215-Interfaces/interfaces-2.gop:
--------------------------------------------------------------------------------
1 | // Now a complete code example to see how the interface is actually used is as follows:
2 |
3 | import (
4 | "math"
5 | )
6 |
7 | type geometry interface {
8 | area() float64
9 | perim() float64
10 | }
11 |
12 | type rect struct {
13 | width float64
14 | height float64
15 | }
16 |
17 | type circle struct {
18 | radius float64
19 | }
20 |
21 | func (r rect) area() float64 {
22 | return r.width * r.height
23 | }
24 |
25 | func (r rect) perim() float64 {
26 | return 2 * (r.width + r.height)
27 | }
28 |
29 | func (c circle) area() float64 {
30 | return math.Pi * c.radius * c.radius
31 | }
32 |
33 | func (c circle) perim() float64 {
34 | return 2 * math.Pi * c.radius
35 | }
36 |
37 | func measure(g geometry) {
38 | println(g)
39 | println("area:", g.area())
40 | println("perimeter:", g.perim())
41 | }
42 |
43 | r := rect{width: 6, height: 9}
44 | c := circle{radius: 2}
45 |
46 | measure(r)
47 | measure(c)
48 |
49 | /*
50 | Run results:
51 |
52 | {6 9}
53 | area: 54
54 | perimeter: 30
55 | {2}
56 | area: 12.566370614359172
57 | perimeter: 12.566370614359172
58 | */
59 |
--------------------------------------------------------------------------------
/216-Interface-Satisfaction/interface-satisfy-1.gop:
--------------------------------------------------------------------------------
1 | // In XGo, interfaces do not need to be explicitly implemented – i.e. no implement keyword. Instead, interfaces are satisfied implicitly.
2 |
3 | // A type satisfies an interface if it possesses all the methods the interface requires. For example,
4 | // an *os.File satisfies io.Reader, io.Writer, io.Closer, and io.ReadWriter. A *bytes.Buffer satisfies io.Reader, io.Writer, and io.ReadWriter, but does not satisfy io.Closer because it does not have a
5 | // Close method.
6 |
7 | // The assignability rule for interfaces is very simple: an expression may be assigned to
8 | // an interface only if its type satisfies the interface. This rule applies even when the right-hand side is itself an interface. For example:
9 |
10 | import (
11 | "bytes"
12 | "io"
13 | "os"
14 | "time"
15 | )
16 |
17 | var w io.Writer
18 |
19 | w = os.Stdout // OK: *os.File has Write method
20 | w = new(bytes.Buffer) // OK: *bytes.Buffer has Write method
21 | w = time.Second // compile error: time.Duration lacks Write method
22 |
23 | var rwc io.ReadWriteCloser
24 | rwc = os.Stdout // OK: *os.File has Read, Write, Close methods
25 | rwc = new(bytes.Buffer) // compile error: *bytes.Buffer lacks Close method
26 |
27 | w = rwc // OK: io.ReadWriteCloser interface has Write method
28 | rwc = w // compile error: io.Writer interface lacks Close method
29 |
30 | // Because io.ReadWriter and io.ReadWriteCloser include all the methods of io.Writer, any type that
31 | // satisfies io.ReadWriter or io.ReadWriteCloser necessarily satisfies io.Writer.
32 |
--------------------------------------------------------------------------------
/216-Interface-Satisfaction/interface-satisfy-2.gop:
--------------------------------------------------------------------------------
1 | // Like an envelope that wraps and conceals the letter it holds, an interface wraps and conceals
2 | // the concrete type and value that it holds. Only the methods revealed by the interface type may
3 | // be called, even if the concrete type has others. For example:
4 |
5 | import (
6 | "io"
7 | "os"
8 | )
9 |
10 | os.Stdout.Write([]byte("hello")) // OK: *os.File has Write method
11 | os.Stdout.Close() // OK: *os.File has Close method
12 | var w io.Writer
13 | w = os.Stdout
14 | w.Write([]byte("hello")) // OK: io.Writer has Write method
15 | w.Close() // compile error: io.Writer lacks Close method
16 |
--------------------------------------------------------------------------------
/216-Interface-Satisfaction/interface-satisfy-3.gop:
--------------------------------------------------------------------------------
1 | // A concrete type may satisfy many unrelated interfaces. Consider a program that organizes or
2 | // sells digitized cultural artifacts like music, films, and books. It might define the following set
3 | // of concrete types:
4 |
5 | // Album
6 | // Book
7 | // Movie
8 | // Magazine
9 | // Podcast
10 | // TVEpisode
11 | // Track
12 |
--------------------------------------------------------------------------------
/216-Interface-Satisfaction/interface-satisfy-4.gop:
--------------------------------------------------------------------------------
1 | // We can express each abstraction of interest as an interface. Some properties are common to all
2 | // artifacts, such as a title, a creation date, and a list of creators (author s or artists).
3 |
4 | import "time"
5 |
6 | type Artifact interface {
7 | Title() string
8 | Creators() []string
9 | Created() time.Time
10 | }
11 |
--------------------------------------------------------------------------------
/216-Interface-Satisfaction/interface-satisfy-5.gop:
--------------------------------------------------------------------------------
1 | // Other properties are restricted to certain types of artifacts. Properties of the printed word are
2 | // relevant only to books and magazines, where as only movies and TV episodes have a screen
3 | // resolution.
4 |
5 | import (
6 | "io"
7 | "time"
8 | )
9 |
10 | type Text interface {
11 | Pages() int
12 | Words() int
13 | PageSize() int
14 | }
15 |
16 | type Audio interface {
17 | Stream() (io.ReadCloser, error)
18 | RunningTime() time.Duration
19 | Format() string // e.g., "MP3", "WAV"
20 | }
21 |
22 | type Video interface {
23 | Stream() (io.ReadCloser, error)
24 | RunningTime() time.Duration
25 | Format() string // e.g., "MP4", "WMV"
26 | Resolution() (x, y int)
27 | }
28 |
--------------------------------------------------------------------------------
/216-Interface-Satisfaction/interface-satisfy-6.gop:
--------------------------------------------------------------------------------
1 | // These interfaces are but one useful way to group related concrete types together and express
2 | // the facets they share in common. We may discover other groupings later. For example, if we
3 | // find we need to handle Audio and Video items in the same way, we can define a Streamer
4 | // interface to represent their common asects without changing any existing type declarations.
5 |
6 | import (
7 | "io"
8 | "time"
9 | )
10 |
11 | type Streamer interface {
12 | Stream() (io.ReadCloser, error)
13 | RunningTime() time.Duration
14 | Format() string
15 | }
16 |
17 | // Each grouping of concrete types based on their shared behaviors can be expressed as an interface type. Unlike class-based languages, in which the set of interfaces satisfied by a class is
18 | // explicit, in Go we can define new abstractions or groupings of interest when we need them,
19 | // without modifying the declaration of the concrete type. This is particularly useful when the
20 | // concrete type comes from a package written by a different author. Of course, there do need to
21 | // be underlying commonalities in the concrete types.
22 |
--------------------------------------------------------------------------------
/217-Interface-Values/interface-values-1.gop:
--------------------------------------------------------------------------------
1 | // The value of the interface type and the interface value are two different concepts.
2 |
3 | // XGo is a statically typed programming language, types are a compile time concept, so a type is not a value.
4 | // The value of the type descriptor provides information about each type, such as its name and methods.
5 | // In an interface value, the type component is represented by the appropriate type descriptor.
6 |
7 | // In XGo, interfaces variables are always initialize well-defined value as other type variables.
8 | // The zero value for an interface has both its type and value components set to nil, for example:
9 |
10 | import (
11 | "bytes"
12 | "io"
13 | "os"
14 | )
15 |
16 | var w io.Writer
17 |
18 | println w == nil
19 |
20 | var w2 io.Writer = os.Stdout
21 |
22 | w2 = nil // same as w, both w and w2 interface variable has zero value
23 |
24 | println w2 == nil
25 |
26 | var r *bytes.Reader
27 |
28 | println r == nil
29 |
30 | var r2 io.Reader = r // r2 has non-zero interface value. Its dynamic type is *bytes.Reader and its dynamic value is nil. So the result of r2 == nil is false
31 |
32 | if r2 != nil {
33 | slice := []byte{}
34 | r2.Read(slice) // panic: runtime error: invalid memory address or nil pointer dereference
35 | }
36 |
37 | // The interface variable w has zero value. So both its dynamic type and its dynamic value are nil. In this case, the result of if w == nil is true.
38 | // The interface variable r2 has non-zero value. Its dynamic type is *bytes.Reader and its dynamic value is nil. So the result of r2 == nil is false.
39 |
--------------------------------------------------------------------------------
/217-Interface-Values/interface-values-2.gop:
--------------------------------------------------------------------------------
1 | // Interface values may be compared using == and !=. Two interface values are equal if both are
2 | // nil, or if their dynamic types are identical and their dynamic values are equal according to the
3 | // usual behavior of == for that type. Because interface values are comparable, they may be used
4 | // as the keys of a map or as the operand of a switch statement.
5 |
6 | // However, if two interface values are compared and have the same dynamic type, but that type
7 | // is not comparable (a slice, for instance), then the comparison fails with a panic. For example
8 |
9 | var x = []any{1, 2, 3}
10 | var y = []any{1, 2, 3}
11 |
12 | println(x == y) // panic: comparing uncomparable type []int
13 |
14 | // In this respect, interface types are unusual. Other types are either safely comparable (like
15 | // basic types and pointers) or not comparable at all (like slices, maps, and functions), but when
16 | // comparing interface values or aggregate types that contain interface values, we must be aware
17 | // of the potential for a panic. A similar risk exists when using interfaces as map keys or switch
18 | // operands. Only compare interface values if you are certain that they contain dynamic values
19 | // of comparable types.
20 |
--------------------------------------------------------------------------------
/217-Interface-Values/interface-values-3.gop:
--------------------------------------------------------------------------------
1 | // When handling errors, or during debugging, it is often helpful to report the dynamic type of
2 | // an interface value. For that, we use the fmt package’s %T verb:
3 |
4 | import (
5 | "bytes"
6 | "fmt"
7 | "io"
8 | "os"
9 | )
10 |
11 | var w io.Writer
12 |
13 | fmt.printf("%T\n", w) // ""
14 |
15 | w = os.Stdout
16 | fmt.printf("%T\n", w) // "*os.File"
17 |
18 | w = new(bytes.Buffer)
19 | fmt.printf("%T\n", w) // "*bytes.Buffer"
20 |
21 | // Internally, fmt uses reflection to obtain the name of the int erface’s dynamic type.
22 |
--------------------------------------------------------------------------------
/218-The-error-Interface/error-1.gop:
--------------------------------------------------------------------------------
1 | // error interface has a single method that returns an error message:
2 |
3 | type error interface {
4 | Error() string
5 | }
6 |
--------------------------------------------------------------------------------
/218-The-error-Interface/error-2.gop:
--------------------------------------------------------------------------------
1 | // The simplest way to create an error is by calling errors.New, which returns a new error for
2 | // a given error message. The entire errors package is only four lines long:
3 |
4 | package errors
5 |
6 | func New(text string) error {
7 | return &errorString{text}
8 | }
9 |
10 | type errorString struct {
11 | text string
12 | }
13 |
14 | func (e *errorString) Error() string {
15 | return e.text
16 | }
17 |
18 | // The underlying type of errorString is a struct, not a string , to protect its representation from
19 | // inadvertent (or premeditated) updates. And the reason that the pointer type *errorString,
20 | // not errorString alone, satisfies the error interface is so that every call to New allocates a distinct error instance that is equal to no other.
21 |
--------------------------------------------------------------------------------
/218-The-error-Interface/error-3.gop:
--------------------------------------------------------------------------------
1 | // We would not want a distinguished error such as io.EOF to compare equal to one that merely happened to have the same message.
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | )
7 |
8 | fmt.Println(errors.New("EOF") == errors.New("EOF")) // "false"
9 |
--------------------------------------------------------------------------------
/218-The-error-Interface/error-4.gop:
--------------------------------------------------------------------------------
1 | // Calls to errors.New are relatively infrequent because there’s a convenient wrapper function,
2 | // fmt.Errorf, that does string formatting too.
3 |
4 | package fmt
5 |
6 | import "errors"
7 |
8 | func Sprintf(format string, args ...interface{}) (s string) {
9 | }
10 |
11 | func Errorf(format string, args ...interface{}) error {
12 | return errors.New(Sprintf(format, args...))
13 | }
14 |
--------------------------------------------------------------------------------
/219-Type-Assertions/type-assert-1.gop:
--------------------------------------------------------------------------------
1 | // A type assertion is an operation applied to an interface value. Syntactically, it looks like x.(T),
2 | // where x is an expression of an interface type and T is a type, called the "asserted" type. A type
3 | // assertion checks that the dynamic type of its operand matches the asserted type.
4 |
5 | // There are two possibilities. First, if the asserted type T is a concrete type, then the type assertion checks whether x’s dynamic type is identical to T. If this check succeeds, the result of the
6 | // type assertion is x’s dynamic value, whose type is of course T. In other words, a type assertion
7 | // to a concrete type extracts the concrete value from its operand. If the check fails, then the
8 | // operation panics. For example:
9 |
10 | import (
11 | "bytes"
12 | "io"
13 | "os"
14 | )
15 |
16 | var w io.Writer
17 |
18 | w = os.Stdout
19 | f := w.(*os.File) // success: f == os.Stdout
20 | c := w.(*bytes.Buffer) // panic: interface conversion: io.Writer is *os.File, not *bytes.Buffer
21 | printf "f: %T, c: %T", f, c
22 |
--------------------------------------------------------------------------------
/219-Type-Assertions/type-assert-2.gop:
--------------------------------------------------------------------------------
1 | // Second, if instead the asserted type T is an interface type, then the type assertion checks
2 | // whether x’s dynamic type satisfies T. If this check succeeds, the dynamic value is not extracted;
3 | // the result is still an interface value with the same type and value components, but the result
4 | // has the interface type T. In other words, a type assertion to an interface type changes the type
5 | // of the expression, making a different (and usually larger) set of methods accessible, but it
6 | // preserves the dynamic type and value components inside the interface value.
7 |
8 | import (
9 | "io"
10 | "os"
11 | )
12 |
13 | type ByteCounter struct {
14 | }
15 |
16 | func (b *ByteCounter) Write(bytes []byte) (n int, err error) {
17 | n = 0
18 | err = nil
19 | return
20 | }
21 |
22 | var w io.Writer
23 |
24 | w = os.Stdout
25 | rw := w.(io.ReadWriter) // success: *os.File has both Read and Write
26 | w = new(ByteCounter)
27 | rw = w.(io.ReadWriter) // panic: *ByteCounter has no Read method
28 |
29 | printf "rw: %T\n", rw
30 |
--------------------------------------------------------------------------------
/219-Type-Assertions/type-assert-3.gop:
--------------------------------------------------------------------------------
1 | // No matter what type was asserted, if the operand is a nil interface value, the type assertion
2 | // fails. A type assertion to a less restrictive interface type (one with fewer methods) is rarely
3 | // needed, as it behaves just like an assignment, except in the nil case.
4 |
5 | import (
6 | "io"
7 | "os"
8 | )
9 |
10 | var w io.Writer
11 | var rw io.ReadWriter = os.Stdout
12 |
13 | w = rw // io.ReadWriter is assignable to io.Writer
14 | w = rw.(io.Writer) // fails only if rw == nil
15 |
16 | printf "w: %T\nrw: %T", w, rw
17 |
--------------------------------------------------------------------------------
/219-Type-Assertions/type-assert-4.gop:
--------------------------------------------------------------------------------
1 | // Often we’re not sure of the dynamic type of an interface value, and we’d like to test whether it
2 | // is some particular type. If the type assertion appears in an assignment in which two results are
3 | // expected, such as the following declarations, the operation does not panic on failure but
4 | // instead returns an additional second result, a boolean indicating success:
5 |
6 | import (
7 | "bytes"
8 | "io"
9 | "os"
10 | )
11 |
12 | var w io.Writer = os.Stdout
13 |
14 | f, fok := w.(*os.File) // success: fok, f == os.Stdout
15 | b, bok := w.(*bytes.Buffer) // failure: !bok, b == nil
16 |
17 | printf "f: %T, fok: %v\n", f, fok // f: *os.File, fok: true
18 | printf "b: %v, bok: %v", b, bok // b: , bok: false
19 |
20 | // The second result is conventionally assigned to a variable named ok. If the operation failed,
21 | // ok is false, and the first result is equal to the zero value of the asserted type, which in this
22 | // example is a nil *bytes.Buffer.
23 |
--------------------------------------------------------------------------------
/220-Type-Switches/type-switch.gop:
--------------------------------------------------------------------------------
1 | // A type `switch` compares types instead of values. You
2 | // can use this to discover the type of an interface
3 | // value. In this example, the variable `t` will have the
4 | // type corresponding to its clause.
5 | whatAmI := func(i interface{}) {
6 | switch t := i.(type) {
7 | case bool:
8 | println "I'm a bool"
9 | case int:
10 | println "I'm an int"
11 | default:
12 | printf "Don't know type %T\n", t
13 | }
14 | }
15 | whatAmI true
16 | whatAmI 1
17 | whatAmI "hey"
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tutorials for XGo
2 |
3 | [](https://github.com/goplus/tutorial/actions/workflows/gop.yml)
4 | [](https://tutorial.xgo.dev)
5 | [](https://play.xgo.dev)
6 | [](https://github.com/goplus/gop)
7 |
8 | ## How to run locally
9 |
10 | To see the site locally:
11 |
12 | ```sh
13 | go run .
14 | ```
15 |
16 | and open http://localhost:8000/ in your browser.
17 |
--------------------------------------------------------------------------------
/_deep/Unix-Shebang/shebang:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env -S gop run
2 |
3 | println "Hello, XGo"
4 |
5 | println 1r<<129
6 | println 1/3r+2/7r*2
7 |
8 | arr := [1, 3, 5, 7, 11, 13, 17, 19]
9 | println arr
10 | println [x*x for x in arr, x > 3]
11 |
12 | m := {"Hi": 1, "XGo": 2}
13 | println m
14 | println {v: k for k, v in m}
15 | println [k for k, _ in m]
16 | println [v for v in m]
17 |
--------------------------------------------------------------------------------
/_deep/Using-goplus-in-Go/foo/foo.gop:
--------------------------------------------------------------------------------
1 | package foo
2 |
3 | func ReverseMap(m map[string]int) map[int]string {
4 | return {v: k for k, v in m}
5 | }
6 |
--------------------------------------------------------------------------------
/_deep/Using-goplus-in-Go/foo/foo_test.gop:
--------------------------------------------------------------------------------
1 | package foo
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestReverseMap(t *testing.T) {
8 | out := ReverseMap({"a": 1})
9 | if len(out) != 1 || out[1] != "a" {
10 | t.Fatal("ReverseMap failed:", out)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/_deep/Using-goplus-in-Go/foo/footest_test.gop:
--------------------------------------------------------------------------------
1 | package foo_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/goplus/tutorial/deep/Using-goplus-in-Go/foo"
7 | )
8 |
9 | func TestReverseMap(t *testing.T) {
10 | out := foo.ReverseMap({"b": 2})
11 | if len(out) != 1 || out[2] != "b" {
12 | t.Fatal("ReverseMap failed:", out)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/_todo/02-Var-and-operator/var_and_op.gop:
--------------------------------------------------------------------------------
1 | x := 123.1 - 3i
2 | y, z := "Hello, ", 123
3 | println(y+"complex:", x+1, "int:", z)
4 |
--------------------------------------------------------------------------------
/_todo/03-Import-go-package/import.gop:
--------------------------------------------------------------------------------
1 | import (
2 | "fmt"
3 | "strings"
4 | )
5 |
6 | x := strings.NewReplacer("?", "!").Replace("hello, world???")
7 | fmt.Println("x:", x)
8 |
--------------------------------------------------------------------------------
/_todo/04-Func/func.gop:
--------------------------------------------------------------------------------
1 | import (
2 | "fmt"
3 | "strings"
4 | )
5 |
6 | func foo(x string) string {
7 | return strings.NewReplacer("?", "!").Replace(x)
8 | }
9 |
10 | func printf(format string, args ...interface{}) (n int, err error) {
11 | n, err = fmt.Printf(format, args...)
12 | return
13 | }
14 |
15 | func bar(foo func(string, ...interface{}) (int, error)) {
16 | foo("Hello, %v!\n", "XGo")
17 | }
18 |
19 | bar(printf)
20 | fmt.Println(foo("Hello, world???"))
21 | fmt.Println(printf("Hello, %v\n", "XGo"))
22 |
--------------------------------------------------------------------------------
/_todo/05-Closure/closure.gop:
--------------------------------------------------------------------------------
1 | import "fmt"
2 |
3 | var x = "Hello, world!"
4 |
5 | foo := func(prompt string) (n int, err error) {
6 | n, err = fmt.Println(prompt + x)
7 | return
8 | }
9 |
10 | fmt.Println(foo("x: "))
11 |
12 | printf := func(format string, args ...interface{}) (n int, err error) {
13 | n, err = fmt.Printf(format, args...)
14 | return
15 | }
16 |
17 | bar := func(foo func(string, ...interface{}) (int, error)) {
18 | foo("Hello, %v!\n", "XGo")
19 | }
20 |
21 | bar(printf)
22 | fmt.Println(printf("Hello, %v\n", "XGo"))
23 |
--------------------------------------------------------------------------------
/_todo/06-String-Map-Array-Slice/datastruct.gop:
--------------------------------------------------------------------------------
1 | x := []float64{1, 3.4, 5}
2 | y := map[string]float64{"Hello": 1, "xsw": 3.4}
3 |
4 | a := [...]float64{1, 3.4, 5}
5 | b := [...]float64{1, 3: 3.4, 5}
6 | c := []float64{2: 1.2, 3, 6: 4.5}
7 |
8 | x[1], y["xsw"] = 1.7, 2.8
9 | println("x:", x, "y:", y)
10 | println(`x[1]:`, x[1], `y["xsw"]:`, y["xsw"], `a[1]:`, a[1])
11 |
12 | i := uint16(4)
13 | b[uint32(4)], c[i] = 123, 1.7
14 | println("a:", a, "b:", b, "c:", c)
15 |
16 | arr := [...]float64{1, 2}
17 | title := "Hello,world!" + "2020-05-27"
18 | println(title[:len(title)-len("2006-01-02")], len(arr), arr[1:])
19 |
--------------------------------------------------------------------------------
/_todo/08-SliceLit/slicelit.gop:
--------------------------------------------------------------------------------
1 | x := [1, 3.4] // []float64
2 | println("x:", x)
3 |
4 | y := [1] // []int
5 | println("y:", y)
6 |
7 | z := [1+2i, "xsw"] // []interface{}
8 | println("z:", z)
9 |
10 | println([1, 3.4, 3+4i]) // []complex128
11 | println([5+6i]) // []complex128
12 | println(["xsw", 3]) // []interface{}
13 |
14 | println("empty slice:", []) // []interface{}
15 |
--------------------------------------------------------------------------------
/_todo/09-IfElse-SwitchCase/flow.gop:
--------------------------------------------------------------------------------
1 | x := 0
2 | if t := false; t {
3 | x = 3
4 | } else {
5 | x = 5
6 | }
7 | println("x:", x)
8 |
9 | x = 0
10 | switch s := "Hello"; s {
11 | default:
12 | x = 7
13 | case "world", "hi":
14 | x = 5
15 | case "xsw":
16 | x = 3
17 | }
18 | println("x:", x)
19 |
20 | v := "Hello"
21 | switch {
22 | case v == "xsw":
23 | x = 3
24 | case v == "Hello", v == "world":
25 | x = 9
26 | default:
27 | x = 7
28 | }
29 | println("x:", x)
30 |
31 | v = "Hello"
32 | switch {
33 | case v == "xsw":
34 | x = 3
35 | case v == "hi", v == "world":
36 | x = 9
37 | default:
38 | x = 11
39 | }
40 | println("x:", x)
41 |
42 | switch v {
43 | case "Hello":
44 | println(v)
45 | fallthrough
46 | case "hi":
47 | println(v)
48 | fallthrough
49 | default:
50 | println(v)
51 | }
52 |
53 | z := 3
54 | switch {
55 | case z < 10:
56 | println(z)
57 | fallthrough
58 | case z == 10:
59 | println(z)
60 | fallthrough
61 | case z > 10:
62 | println(z)
63 | fallthrough
64 | default:
65 | println(z)
66 | }
67 |
--------------------------------------------------------------------------------
/_todo/10-List-comprehension/list_comprehens.gop:
--------------------------------------------------------------------------------
1 | y := [x*x for x in [1, 3, 5, 7, 11]]
2 | println(y)
3 |
4 | y = [x*x for x in [1, 3, 5, 7, 11] if x > 3]
5 | println(y)
6 |
7 | z := [i+v for i, v in [1, 3, 5, 7, 11] if i%2 == 1]
8 | println(z)
9 |
10 | println([k+","+s for k, s in {"Hello": "xsw", "Hi": "XGo"}])
11 |
12 | arr := [1, 2, 3, 4, 5, 6]
13 | x := [[a, b] for a in arr if a < b for b in arr if b > 2]
14 | println("x:", x)
15 |
--------------------------------------------------------------------------------
/_todo/11-Map-comprehension/map_comprehens.gop:
--------------------------------------------------------------------------------
1 | y := {x: i for i, x in [1, 3, 5, 7, 11]}
2 | println(y)
3 |
4 | y = {x: i for i, x in [1, 3, 5, 7, 11] if i%2 == 1}
5 | println(y)
6 |
7 | z := {v: k for k, v in {1: "Hello", 3: "Hi", 5: "xsw", 7: "XGo"} if k > 3}
8 | println(z)
9 |
--------------------------------------------------------------------------------
/_todo/12-Select-comprehension/select.gop:
--------------------------------------------------------------------------------
1 | a := [2, 3, 5, 7, 9, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59]
2 | where := {i for i, v in a if v == 19}
3 | println("19 is at index", where)
4 |
5 | if at, ok := {i for i, v in a if v == 37}; ok {
6 | println("37 is found! index =", at)
7 | }
8 |
9 | println("first element of a is:", {v for v in a})
10 |
--------------------------------------------------------------------------------
/_todo/12-Select-comprehension2/findscore.gop:
--------------------------------------------------------------------------------
1 | type student struct {
2 | name string
3 | score int
4 | }
5 |
6 | func findScore(a []student, name string) (score int, ok bool) {
7 | return {x.score for x in a if x.name == name}
8 | }
9 |
10 | a := [student{"ken", 95}, student{"john", 90}, student{"tom", 58}]
11 | println(findScore(a, "tom"))
12 |
--------------------------------------------------------------------------------
/_todo/13-Exists-comprehension/exists.gop:
--------------------------------------------------------------------------------
1 | type student struct {
2 | name string
3 | score int
4 | }
5 |
6 | a := [student{"du", 84}, student{"wang", 70}, student{"ken", 100}]
7 |
8 | hasFullMark := {for x in a if x.score == 100}
9 | println("is any student full mark:", hasFullMark)
10 |
11 | hasFailed := {for x in a if x.score < 60}
12 | println("is any student failed:", hasFailed)
13 |
--------------------------------------------------------------------------------
/_todo/15-ErrWrap/err_wrap.gop:
--------------------------------------------------------------------------------
1 | import (
2 | "strconv"
3 | )
4 |
5 | func add(x, y string) (int, error) {
6 | return strconv.Atoi(x)? + strconv.Atoi(y)?, nil
7 | }
8 |
9 | func addSafe(x, y string) int {
10 | return strconv.Atoi(x)?:0 + strconv.Atoi(y)?:0
11 | }
12 |
13 | println(`add("100", "23"):`, add("100", "23")!)
14 |
15 | sum, err := add("10", "abc")
16 | println(`add("10", "abc"):`, sum, err)
17 |
18 | println(`addSafe("10", "abc"):`, addSafe("10", "abc"))
19 |
--------------------------------------------------------------------------------
/_todo/16-Fib/fib.gop:
--------------------------------------------------------------------------------
1 | func fib(n int) int {
2 | if n == 0 {
3 | return 0
4 | }
5 | if n == 1 {
6 | return 1
7 | }
8 | return fib(n-1) + fib(n-2)
9 | }
10 |
11 | println `fib(27):`, fib(27)
12 |
--------------------------------------------------------------------------------
/_todo/17-Fibtc/fibtc.gop:
--------------------------------------------------------------------------------
1 | func fibtc(n, a, b int) int {
2 | if n == 0 {
3 | return a
4 | }
5 | if n == 1 {
6 | return b
7 | }
8 | return fibtc(n-1, b, a+b)
9 | }
10 |
11 | println `fibtc(27):`, fibtc(27, 0, 1)
12 |
--------------------------------------------------------------------------------
/_todo/18-Rational/rational.gop:
--------------------------------------------------------------------------------
1 | import "math/big"
2 |
3 | var a bigint = 1r << 65 // bigint, large than int64
4 | var b bigrat = 4/5r // bigrat
5 | c := b - 1/3r + 3*1/2r // bigrat
6 | println(a, b, c)
7 |
8 | var x *big.Int = 1r << 65 // (1r << 65) is untyped bigint, and can be assigned to *big.Int
9 | var y *big.Rat = 4/5r
10 | println(x, y)
11 |
12 | a = new(big.Int).Abs(-265r)
13 | println("abs(-265r):", a)
14 |
--------------------------------------------------------------------------------
/_todo/19-IncDec/inc_dec.gop:
--------------------------------------------------------------------------------
1 | a, b := 2, 3
2 | a++
3 | b--
4 | println(a, b)
5 |
--------------------------------------------------------------------------------
/_todo/21-Break-continue-goto/flow.gop:
--------------------------------------------------------------------------------
1 | println("start")
2 |
3 | goto L
4 | println("before")
5 | L:
6 | println("over")
7 | i := 0
8 | L2:
9 | if i < 3 {
10 | println(i)
11 | i++
12 | goto L2
13 | }
14 | println("over")
15 |
16 | sum := 0
17 | arr := [1, 3, 5, 7, 11, 13, 17]
18 | for i = 0; i < len(arr); i++ {
19 | if arr[i] < 3 {
20 | continue
21 | }
22 | if arr[i] > 11 {
23 | break
24 | }
25 | sum += arr[i]
26 | }
27 | println("sum(3,5,7,11):", sum == 26, sum)
28 | sum = 0
29 | L3:
30 | for i = 0; i < len(arr); i++ {
31 | if arr[i] < 3 {
32 | continue L3
33 | }
34 | if arr[i] > 11 {
35 | break L3
36 | }
37 | sum += arr[i]
38 | }
39 | println("sum(3,5,7,11):", sum == 26, sum)
40 |
41 | z := 3
42 | v := "Hello"
43 | switch z {
44 | case 3:
45 | if v == "Hello" {
46 | println("break")
47 | break
48 | }
49 | println("break fail")
50 | default:
51 | println(z)
52 | }
53 | L4:
54 | switch z {
55 | case 3:
56 | if v == "Hello" {
57 | println("break")
58 | break L4
59 | }
60 | println("break fail")
61 | default:
62 | println(z)
63 | }
64 |
--------------------------------------------------------------------------------
/_todo/22-For-loop/for.gop:
--------------------------------------------------------------------------------
1 | // -----------------------------------------------------------------------------
2 | // for with in
3 |
4 | sum := 0
5 | for x in [1, 3, 5, 7, 11, 13, 17] if x > 3 {
6 | sum += x
7 | }
8 | println("sum(5,7,11,13,17):", sum)
9 |
10 | fns := make([]func() int, 3)
11 | for i, x in [3, 15, 777] {
12 | v := x
13 | fns[i] = func() int {
14 | return v
15 | }
16 | }
17 | println("values:", fns[0](), fns[1](), fns[2]())
18 |
19 | // -----------------------------------------------------------------------------
20 | // for with range with define tok (k,v := range x)
21 |
22 | sum = 0
23 | for _, x := range [1, 3, 5, 7, 11, 13, 17] {
24 | if x > 3 {
25 | sum += x
26 | }
27 | }
28 | println("sum(5,7,11,13,17):", sum)
29 |
30 | sum = 0
31 | for i, x := range [3, 15, 777] {
32 | v := x
33 | fns[i] = func() int {
34 | return v
35 | }
36 | }
37 | println("values:", fns[0](), fns[1](), fns[2]())
38 |
39 | // -----------------------------------------------------------------------------
40 | // for with range with assign tok (k,v = range x)
41 |
42 | sum = 0
43 | x := 0
44 | for _, x = range [1, 3, 5, 7, 11, 13, 17] {
45 | if x > 3 {
46 | sum += x
47 | }
48 | }
49 | println("x:", x, x == 17)
50 | println("sum(5,7,11,13,17):", sum)
51 |
52 | // -----------------------------------------------------------------------------
53 | // normal for
54 |
55 | sum = 0
56 | arr := [1, 3, 5, 7, 11, 13, 17]
57 | i := 10
58 | for i = 0; i < len(arr); i++ {
59 | if arr[i] > 3 {
60 | sum += arr[i]
61 | }
62 | }
63 | println("sum(5,7,11,13,17):", sum)
64 |
65 | /* TODO:
66 | arr = [3, 15, 777]
67 | sum = 0
68 | for i = 0; i < len(arr); i++ {
69 | v := arr[i]
70 | fns[i] = func() int {
71 | return v
72 | }
73 | }
74 | println("values:", fns[0](), fns[1](), fns[2]())
75 | */
76 |
77 | // -----------------------------------------------------------------------------
78 |
--------------------------------------------------------------------------------
/_todo/23-Defer/defer.gop:
--------------------------------------------------------------------------------
1 | func f() (x int) {
2 | defer func() {
3 | x = 3
4 | }()
5 | return 1
6 | }
7 |
8 | func g() (x int) {
9 | defer func() {
10 | x = 3
11 | }()
12 | x = 1
13 | return
14 | }
15 |
16 | func h() (x int) {
17 | for i in [3, 2, 1] {
18 | v := i
19 | defer func() {
20 | x = v
21 | }()
22 | }
23 | return
24 | }
25 |
26 | println("f-x:", f())
27 | println("g-x:", g())
28 | println("h-x:", h())
29 |
30 | defer println(println("Hello, defer"))
31 | println("Hello, XGo")
32 |
--------------------------------------------------------------------------------
/_todo/24-Goroutine/goroutine.gop:
--------------------------------------------------------------------------------
1 | import "time"
2 |
3 | go func() {
4 | println("Hello, goroutine!")
5 | }()
6 |
7 | time.Sleep(1e8)
8 |
--------------------------------------------------------------------------------
/_todo/25-Struct/struct.gop:
--------------------------------------------------------------------------------
1 | a := struct {
2 | A int
3 | B string
4 | }{1, "Hello"}
5 |
6 | println(a)
7 |
8 | b := &struct {
9 | A int
10 | B string
11 | }{1, "Hello"}
12 |
13 | c := &struct {
14 | a int
15 | b string
16 | }{1, "Hello"}
17 |
18 | println(b)
19 |
20 | a.A, a.B = 1, "Hi"
21 | println(a)
22 |
23 | b.A, b.B = 2, "Hi2"
24 | println(b)
25 |
26 | c.a, c.b = 3, "Hi3"
27 | println(c)
28 |
--------------------------------------------------------------------------------
/_todo/26-Method/method.gop:
--------------------------------------------------------------------------------
1 | type Person struct {
2 | Name string
3 | Age int
4 | Friends []string
5 | }
6 |
7 | func (p *Person) SetName(name string) {
8 | p.Name = name
9 | println(p.Name)
10 | }
11 |
12 | func (p *Person) SetAge(age int) {
13 | age = age + 5
14 | p.Age = age
15 | println(p.Age)
16 | }
17 |
18 | func (p *Person) AddFriends(args ...string) {
19 | p.Friends = append(p.Friends, args...)
20 | }
21 |
22 | type M int
23 |
24 | func (m M) Foo() {
25 | println("foo", m)
26 | }
27 |
28 | p := Person{
29 | Name: "bar",
30 | Age: 30,
31 | }
32 |
33 | p.Name, p.Age = "bar2", 31
34 |
35 | p.SetName("foo")
36 | p.SetAge(32)
37 | p.AddFriends("a", "b", "c")
38 |
39 | a := int32(0)
40 | m := M(a)
41 | m.Foo()
42 |
43 | println(p.Name)
44 | println(p.Age)
45 | println(p.Friends)
46 | println(m)
47 |
--------------------------------------------------------------------------------
/_todo/27-Func-Set/func.gop:
--------------------------------------------------------------------------------
1 | func A(a *int, c *struct {
2 | b *int
3 | m map[string]*int
4 | s []*int
5 | }) {
6 | *a = 5
7 | *c.b = 3
8 | *c.m["foo"] = 7
9 | *c.s[0] = 9
10 | }
11 |
12 | func Index() int {
13 | return 0
14 | }
15 |
16 | a1 := 6
17 | a2 := 6
18 | a3 := 6
19 | c := struct {
20 | b *int
21 | m map[string]*int
22 | s []*int
23 | }{
24 | b: &a1,
25 | m: map[string]*int{
26 | "foo": &a2,
27 | },
28 | s: []*int{&a3},
29 | }
30 | A(&a1, &c)
31 | *c.m["foo"] = 8
32 | *c.s[0] = 10
33 | *c.s[Index()] = 11
34 | println(a1, *c.b, *c.m["foo"], *c.s[0])
35 |
--------------------------------------------------------------------------------
/_todo/28-Chan/chan.gop:
--------------------------------------------------------------------------------
1 | c := make(chan int, 10)
2 | c <- 3
3 | close(c)
4 |
5 | d := <-c
6 | e, ok := <-c
7 |
8 | println(d)
9 | println(e, ok)
10 |
--------------------------------------------------------------------------------
/_todo/29-CompareToNil/ref.gop:
--------------------------------------------------------------------------------
1 | func foo() []int {
2 | return nil
3 | }
4 |
5 | func foo1() map[int]int {
6 | return make(map[int]int, 10)
7 | }
8 |
9 | func foo2() chan int {
10 | return make(chan int, 10)
11 | }
12 |
13 | func foo3() *int {
14 | return nil
15 | }
16 |
17 | println(foo() == nil)
18 | println(nil == foo())
19 | println(foo() != nil)
20 | println(nil != foo())
21 |
22 | println(foo1() == nil)
23 | println(nil == foo1())
24 | println(foo1() != nil)
25 | println(nil != foo1())
26 |
27 | println(foo2() == nil)
28 | println(nil == foo2())
29 | println(foo2() != nil)
30 | println(nil != foo2())
31 |
32 | println(foo3() == nil)
33 | println(nil == foo3())
34 | println(foo3() != nil)
35 | println(nil != foo3())
36 |
--------------------------------------------------------------------------------
/_todo/30-Recover/recover.gop:
--------------------------------------------------------------------------------
1 | defer func() {
2 | var err interface{}
3 | if err = recover(); err != nil {
4 | println(err)
5 | }
6 | }()
7 |
8 | panic("hello recover")
9 |
--------------------------------------------------------------------------------
/_todo/31-Builtin-Typecast/builtin_and_typecast.gop:
--------------------------------------------------------------------------------
1 | n := 2
2 | a := make([]int, uint64(n))
3 | a = append(a, 1, 2, 3)
4 | println(a)
5 | println("len:", len(a))
6 |
7 | b := make([]int, 0, uint16(4))
8 | c := [1, 2, 3]
9 | b = append(b, c...)
10 | println(b)
11 | println("len:", len(b), "cap:", cap(b))
12 |
--------------------------------------------------------------------------------
/_todo/32-Import-gop-package/import_gop_pkg.gop:
--------------------------------------------------------------------------------
1 | import "github.com/goplus/gop/tutorial/14-Using-goplus-in-Go/foo"
2 |
3 | rmap := foo.ReverseMap({"Hi": 1, "Hello": 2})
4 | println(rmap)
5 |
--------------------------------------------------------------------------------
/_todo/33-Interface/shape.gop:
--------------------------------------------------------------------------------
1 | import "math"
2 |
3 | type Shape interface {
4 | Area() float64
5 | }
6 |
7 | type Rect struct {
8 | x, y, w, h float64
9 | }
10 |
11 | func (p *Rect) Area() float64 {
12 | return p.w * p.h
13 | }
14 |
15 | type Circle struct {
16 | x, y, r float64
17 | }
18 |
19 | func (p *Circle) Area() float64 {
20 | return math.Pi * p.r * p.r
21 | }
22 |
23 | func Area(shapes ...Shape) float64 {
24 | s := 0.0
25 | for shape in shapes {
26 | s += shape.Area()
27 | }
28 | return s
29 | }
30 |
31 | rect := &Rect{0, 0, 2, 5}
32 | circle := &Circle{0, 0, 3}
33 | println("area:", Area(circle, rect))
34 |
--------------------------------------------------------------------------------
/_todo/34-Type-assert/type_assert.gop:
--------------------------------------------------------------------------------
1 | func foo(v interface{}) string {
2 | if _, ok := v.(bool); ok {
3 | return "bool"
4 | }
5 | switch v.(type) {
6 | case int:
7 | return "int"
8 | case string:
9 | return "string"
10 | default:
11 | return "unknown"
12 | }
13 | }
14 |
15 | func add(v, delta interface{}) interface{} {
16 | switch a := v.(type) {
17 | case int:
18 | return a + delta.(int)
19 | case float64:
20 | return a + delta.(float64)
21 | case string:
22 | return a + delta.(string)
23 | }
24 | return nil
25 | }
26 |
27 | println(foo(1), foo("Hi"))
28 | println(add(4, 3), add("n", "iu"))
29 |
--------------------------------------------------------------------------------
/_todo/35-Chan-select/select.gop:
--------------------------------------------------------------------------------
1 | var done = make(chan bool, 1)
2 | var exited = make(chan bool, 1)
3 |
4 | func consume(xchg chan int) {
5 | for {
6 | select {
7 | case c := <-xchg:
8 | println(c)
9 | case <-done:
10 | println("done!")
11 | exited <- true
12 | return
13 | }
14 | }
15 | }
16 |
17 | func product(xchg chan int, from chan int) {
18 | for x in from {
19 | xchg <- x
20 | }
21 | done <- true
22 | }
23 |
24 | from := make(chan int, 10)
25 | xchg := make(chan int, 1)
26 | go consume(xchg)
27 | go product(xchg, from)
28 | for i := 1; i <= 10; i++ {
29 | from <- i
30 | }
31 | close(from)
32 | <-exited
33 |
--------------------------------------------------------------------------------
/_todo/36-Auto-Property/autoprop.gop:
--------------------------------------------------------------------------------
1 | import (
2 | "gop/ast/goptest"
3 | )
4 |
5 | script := `
6 | import (
7 | gio "io"
8 | )
9 |
10 | func New() (*Bar, error) {
11 | return nil, gio.EOF
12 | }
13 |
14 | bar, err := New()
15 | if err != nil {
16 | log.Println(err)
17 | }
18 | `
19 |
20 | doc := goptest.New(script)!
21 |
22 | println(doc.any.funcDecl.name)
23 | println(doc.any.importSpec.name)
24 |
--------------------------------------------------------------------------------
/_todo/37-Cmdline/cmdline.gop:
--------------------------------------------------------------------------------
1 | import "os"
2 |
3 | println("args:", os.Args[1:])
4 |
--------------------------------------------------------------------------------
/_todo/38-Overload-operator/overload_op.gop:
--------------------------------------------------------------------------------
1 | import "math/big"
2 |
3 | type MyBigInt struct {
4 | *big.Int
5 | }
6 |
7 | func Int(v *big.Int) MyBigInt {
8 | return MyBigInt{v}
9 | }
10 |
11 | func (a MyBigInt) + (b MyBigInt) MyBigInt {
12 | return MyBigInt{new(big.Int).Add(a.Int, b.Int)}
13 | }
14 |
15 | func (a MyBigInt) += (b MyBigInt) {
16 | a.Int.Add(a.Int, b.Int)
17 | }
18 |
19 | func -(a MyBigInt) MyBigInt {
20 | return MyBigInt{new(big.Int).Neg(a.Int)}
21 | }
22 |
23 | a := Int(1r)
24 | a += Int(2r)
25 | println(a + Int(3r))
26 | println(-a)
27 |
--------------------------------------------------------------------------------
/_todo/39-Lambda-expression/lambda.gop:
--------------------------------------------------------------------------------
1 | func Map(c []float64, t func(float64) float64) []float64 {
2 | return [t(x) for x in c]
3 | }
4 |
5 | println(Map([1.2, 3.5, 6], x => x * x))
6 |
--------------------------------------------------------------------------------
/_todo/40-Deduce-struct-type/deduce.gop:
--------------------------------------------------------------------------------
1 | type Config struct {
2 | Dir string
3 | Level int
4 | }
5 |
6 | type Result struct {
7 | Text string
8 | }
9 |
10 | func foo(conf *Config) *Result {
11 | println("conf:", *conf)
12 | return {Text: "Hello, XGo"}
13 | }
14 |
15 | ret := foo({Dir: "/foo/bar", Level: 1})
16 | println(ret)
17 |
--------------------------------------------------------------------------------
/_todo/41-UDT-RangeForEach/udt_range.gop:
--------------------------------------------------------------------------------
1 | type foo struct {
2 | }
3 |
4 | func (p *foo) Gop_Enum(proc func(key int, val string)) {
5 | proc(3, "Hi")
6 | proc(7, "XGo")
7 | }
8 |
9 | for k, v in new(foo) {
10 | println(k, v)
11 | }
12 |
13 | println({v: k for k, v in new(foo)})
14 |
--------------------------------------------------------------------------------
/_todo/42-UDT-RangeIterator/udt_range_iter.gop:
--------------------------------------------------------------------------------
1 | type fooIter struct {
2 | data *foo
3 | idx int
4 | }
5 |
6 | func (p *fooIter) Next() (key int, val string, ok bool) {
7 | if p.idx < len(p.data.key) {
8 | key, val, ok = p.data.key[p.idx], p.data.val[p.idx], true
9 | p.idx++
10 | }
11 | return
12 | }
13 |
14 | type foo struct {
15 | key []int
16 | val []string
17 | }
18 |
19 | func newFoo() *foo {
20 | return &foo{key: [3, 7], val: ["Hi", "XGo"]}
21 | }
22 |
23 | func (p *foo) Gop_Enum() *fooIter {
24 | return &fooIter{data: p}
25 | }
26 |
27 | obj := newFoo()
28 | for k, v in obj {
29 | println(k, v)
30 | }
31 |
32 | println({v: k for k, v in obj})
33 |
--------------------------------------------------------------------------------
/_todo/43-RangeExpr/rangeexpr.gop:
--------------------------------------------------------------------------------
1 | println "---------------------------"
2 |
3 | for i in :10 {
4 | println(i)
5 | }
6 |
7 | println "---------------------------"
8 |
9 | for i in 9:-1:-1 {
10 | println(i)
11 | }
12 |
13 | println "---------------------------"
14 |
15 | for i := range :10:2 {
16 | println(i)
17 | }
18 |
19 | println "---------------------------"
20 |
21 | for i := range 1:10:3 {
22 | println(i)
23 | }
24 |
25 | println "---------------------------"
26 |
27 | for range :10 {
28 | println("Range expression")
29 | }
30 |
31 | println "---------------------------"
32 |
--------------------------------------------------------------------------------
/dummy/dummy.go:
--------------------------------------------------------------------------------
1 | package dummy
2 |
3 | import (
4 | _ "github.com/goplus/gop"
5 | )
6 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/goplus/tutorial
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/goplus/gop v1.2.5
7 | github.com/howeyc/fsnotify v0.9.0
8 | github.com/russross/blackfriday/v2 v2.1.0
9 | )
10 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 | github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
4 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
5 | github.com/goplus/c2go v0.7.25/go.mod h1:e9oe4jDVhGFMJLEGmPSrVkLuXbLZAEmAu0/uD6fSz5E=
6 | github.com/goplus/gogen v1.15.0/go.mod h1:92qEzVgv7y8JEFICWG9GvYI5IzfEkxYdsA1DbmnTkqk=
7 | github.com/goplus/gogen v1.15.1 h1:iz/fFpOeldjwmnjLzEdNsZF2mCf+sOHJavbAvV3o7sY=
8 | github.com/goplus/gogen v1.15.1/go.mod h1:92qEzVgv7y8JEFICWG9GvYI5IzfEkxYdsA1DbmnTkqk=
9 | github.com/goplus/gop v1.2.5 h1:kBCcvJ7ONt3IqTUTNGb+n9JO1nwTp5STg1UUSEfvz/k=
10 | github.com/goplus/gop v1.2.5/go.mod h1:5rLlryvlZhWLIBw1Rok8s0uvyO54vcYw/7sFLhqtNTc=
11 | github.com/goplus/mod v0.13.9 h1:B9zZoHi2AzMltTSOFqZNVjqGlSMlhhNTWwEzVqhTQzg=
12 | github.com/goplus/mod v0.13.9/go.mod h1:MibsLSftGmxaQq78YzUzNviyFwB9RtpMaoscufvEKH4=
13 | github.com/howeyc/fsnotify v0.9.0 h1:0gtV5JmOKH4A8SsFxG2BczSeXWWPvcMT0euZt5gDAxY=
14 | github.com/howeyc/fsnotify v0.9.0/go.mod h1:41HzSPxBGeFRQKEEwgh49TRw/nKBsYZ2cF1OzPjSJsA=
15 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
16 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
17 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
18 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
19 | github.com/qiniu/x v1.13.9 h1:OUcQZSze1oTO5pzrhsepTUvEb9K9WtOiqZomWffFGWE=
20 | github.com/qiniu/x v1.13.9/go.mod h1:INZ2TSWSJVWO/RuELQROERcslBwVgFG7MkTfEdaQz9E=
21 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
22 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
23 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
24 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
25 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
26 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
27 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
28 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
29 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
30 | golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
31 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
32 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
33 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
34 | golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
35 | golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
36 | golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
37 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
38 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
39 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
40 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
41 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
42 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
43 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
44 | golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
45 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
46 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
47 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
48 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
49 | golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
50 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
51 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
52 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
53 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
54 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
55 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
56 | golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
57 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
58 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
59 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
60 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
61 | golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
62 | golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
63 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
64 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
65 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
66 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
67 | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
68 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
69 | golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
70 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
71 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
72 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
73 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
74 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
75 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
76 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
77 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
78 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
79 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
80 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
81 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
82 | golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
83 | golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
84 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
85 |
--------------------------------------------------------------------------------
/internal/watch.go:
--------------------------------------------------------------------------------
1 | package internal
2 |
3 | import (
4 | "fmt"
5 | "io/fs"
6 | "log"
7 | "os"
8 | "path/filepath"
9 |
10 | "github.com/howeyc/fsnotify"
11 | )
12 |
13 | var fileTypesToWatch = []string{
14 | ".gop",
15 | }
16 |
17 | type Watcher struct {
18 | eventHandler func(string)
19 | watch *fsnotify.Watcher
20 | }
21 |
22 | func NewWatcher(handler func(string)) (*Watcher, error) {
23 | watch, err := fsnotify.NewWatcher()
24 | if err != nil {
25 | return nil, err
26 | }
27 |
28 | w := Watcher{
29 | eventHandler: handler,
30 | watch: watch,
31 | }
32 |
33 | go func() {
34 | for {
35 | select {
36 | case e := <-w.watch.Event:
37 | if w.shallFireEvent(e) {
38 | w.handleEvent(e)
39 | }
40 | case err := <-w.watch.Error:
41 | log.Println("failed to watch file changes:", err)
42 | }
43 | }
44 | }()
45 |
46 | return &w, nil
47 | }
48 |
49 | func (w Watcher) Watch(dir string) error {
50 | return filepath.Walk(dir, w.processWalked)
51 | }
52 |
53 | func (w Watcher) handleEvent(e *fsnotify.FileEvent) {
54 | if e.IsCreate() {
55 | w.handleCreateEvent(e)
56 | return
57 | }
58 |
59 | if w.isFileConcerned(e.Name) {
60 | w.eventHandler(e.Name)
61 | }
62 | }
63 |
64 | func (w Watcher) handleCreateEvent(e *fsnotify.FileEvent) {
65 | info, err := os.Stat(e.Name)
66 | if err != nil {
67 | log.Printf("failed to stat %q: %v\n", e.Name, err)
68 | return
69 | }
70 |
71 | if info.IsDir() {
72 | if err := filepath.Walk(e.Name, w.processWalked); err != nil {
73 | log.Printf("failed to watch %q: %v\n", e.Name, err)
74 | }
75 | } else if w.isFileConcerned(e.Name) {
76 | fmt.Println("watching", e.Name)
77 | if err := w.watch.Watch(e.Name); err != nil {
78 | log.Printf("failed to watch %q: %v\n", e.Name, err)
79 | }
80 | w.eventHandler(e.Name)
81 | }
82 | }
83 |
84 | func (w Watcher) isFileConcerned(file string) bool {
85 | ext := filepath.Ext(file)
86 |
87 | for _, tp := range fileTypesToWatch {
88 | if tp == ext {
89 | return true
90 | }
91 | }
92 |
93 | return false
94 | }
95 |
96 | func (w Watcher) processWalked(path string, info fs.FileInfo, err error) error {
97 | if err != nil {
98 | return err
99 | }
100 |
101 | if w.isFileConcerned(path) {
102 | return w.watch.Watch(path)
103 | }
104 |
105 | return nil
106 | }
107 |
108 | func (w Watcher) shallFireEvent(e *fsnotify.FileEvent) bool {
109 | // why we put IsAttrib() at the first,
110 | // because modify on vim fires the attrib event and modify event.
111 | if e.IsAttrib() {
112 | return false
113 | }
114 | if e.IsCreate() {
115 | return true
116 | }
117 | if e.IsDelete() {
118 | return true
119 | }
120 | if e.IsModify() {
121 | return true
122 | }
123 | if e.IsRename() {
124 | return true
125 | }
126 |
127 | return false
128 | }
129 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "flag"
6 | "fmt"
7 | "log"
8 | "net/http"
9 | "os"
10 | "path"
11 | "path/filepath"
12 | "strconv"
13 | "strings"
14 | "sync"
15 | "sync/atomic"
16 | "text/template"
17 | "unsafe"
18 |
19 | gohtml "html"
20 |
21 | "github.com/goplus/tutorial/internal"
22 | "github.com/russross/blackfriday/v2"
23 | )
24 |
25 | const (
26 | chNumLen = 3
27 | )
28 |
29 | var (
30 | headerTempl string
31 | footerTempl string
32 | exampleTmpl *template.Template
33 | )
34 |
35 | func init() {
36 | headerTempl = mustReadFile("templates/header.tmpl")
37 | footerTempl = mustReadFile("templates/footer.tmpl")
38 | exampleTmpl = template.New("example")
39 | _, err := exampleTmpl.Parse(headerTempl)
40 | check(err)
41 | _, err = exampleTmpl.Parse(footerTempl)
42 | check(err)
43 | _, err = exampleTmpl.Parse(mustReadFile("templates/example.tmpl"))
44 | check(err)
45 | }
46 |
47 | func check(err error) {
48 | if err != nil {
49 | panic(err)
50 | }
51 | }
52 |
53 | func mustReadFile(path string) string {
54 | bytes, err := os.ReadFile(path)
55 | check(err)
56 | return string(bytes)
57 | }
58 |
59 | // -----------------------------------------------------------------------------
60 |
61 | type exampleIndex struct {
62 | Path string
63 | Name string
64 | Title string
65 | Prev *exampleIndex
66 | Next *exampleIndex
67 | cache *example
68 | }
69 |
70 | type chapter struct {
71 | Title string
72 | Articles []*exampleIndex
73 | }
74 |
75 | var (
76 | exampleIndexes map[string]*exampleIndex
77 | watcher *internal.Watcher
78 | )
79 |
80 | func listTutorial(dir string) (names []string, err error) {
81 | fis, err := os.ReadDir(dir)
82 | if err != nil {
83 | return
84 | }
85 |
86 | for _, fi := range fis {
87 | if fi.IsDir() {
88 | name := fi.Name()
89 | if len(name) > (chNumLen+1) && name[chNumLen] == '-' {
90 | if _, e := strconv.Atoi(name[:1]); e == nil {
91 | names = append(names, name)
92 | }
93 | }
94 | }
95 | }
96 | return
97 | }
98 |
99 | func renderIndex(tutorial []string) []byte {
100 | indexTmpl := template.New("index")
101 | _, err := indexTmpl.Parse(headerTempl)
102 | check(err)
103 | _, err = indexTmpl.Parse(footerTempl)
104 | check(err)
105 | _, err = indexTmpl.Parse(mustReadFile("templates/index.tmpl"))
106 | check(err)
107 |
108 | var buf bytes.Buffer
109 | var indexes = make(map[string]*exampleIndex, len(tutorial))
110 | var chs []*chapter
111 | var ch *chapter
112 | var prev *exampleIndex
113 | for _, name := range tutorial {
114 | title := name[chNumLen+1:]
115 | titleEsc := strings.ReplaceAll(title, "-", " ")
116 | if strings.HasSuffix(name[:chNumLen], "00") {
117 | ch = &chapter{Title: titleEsc}
118 | chs = append(chs, ch)
119 | continue
120 | }
121 | idx := &exampleIndex{
122 | Path: "/" + strings.ToLower(strings.ReplaceAll(gohtml.UnescapeString(title), "/", "-")),
123 | Name: name,
124 | Title: titleEsc,
125 | }
126 | if ch == nil {
127 | ch = new(chapter)
128 | chs = append(chs, ch)
129 | }
130 | ch.Articles = append(ch.Articles, idx)
131 | indexes[idx.Path] = idx
132 | if prev != nil {
133 | prev.Next = idx
134 | }
135 | idx.Prev = prev
136 | prev = idx
137 | }
138 | exampleIndexes = indexes
139 | err = indexTmpl.Execute(&buf, chs)
140 | check(err)
141 | return buf.Bytes()
142 | }
143 |
144 | // -----------------------------------------------------------------------------
145 |
146 | // Seg is a segment of an example
147 | type Seg struct {
148 | Docs []string
149 | Code []string
150 | DocsRendered string
151 | CodeRendered string
152 | }
153 |
154 | const (
155 | ltNone = -1
156 | ltCode = 0 // ltCode must be 0
157 | ltDoc = 1
158 | ltBlank = 2
159 | )
160 |
161 | func checkLineType(line string) (doc string, lt int) {
162 | doc = strings.TrimSpace(line)
163 | if strings.HasPrefix(doc, "//") {
164 | return strings.TrimPrefix(doc[2:], " "), ltDoc
165 | }
166 | if strings.HasPrefix(doc, "#") && !strings.HasPrefix(doc, "#!") {
167 | doc = "##" + doc
168 | lt = ltDoc
169 | } else if doc == "" {
170 | lt = ltBlank
171 | }
172 | return
173 | }
174 |
175 | func parseSegs(filecontent string) (segs []*Seg) {
176 | source := strings.Split(filecontent, "\n")
177 | lines := make([]string, len(source))
178 | for i, line := range source {
179 | // Convert tabs to spaces for uniform rendering.
180 | lines[i] = strings.ReplaceAll(line, "\t", " ")
181 | }
182 |
183 | var lastSeg *Seg
184 | var lastSeen = ltNone
185 | for _, line := range lines {
186 | trimmed, lt := checkLineType(line)
187 | if lt == ltDoc || (lt == ltBlank && lastSeen == ltDoc) {
188 | if lt == ltDoc {
189 | if lastSeen == ltDoc {
190 | lastSeg.Docs = append(lastSeg.Docs, trimmed)
191 | } else {
192 | lastSeg = &Seg{Docs: []string{trimmed}}
193 | segs = append(segs, lastSeg)
194 | }
195 | }
196 | lastSeen = lt
197 | } else if lt == ltCode || lastSeen == ltCode {
198 | if lastSeen != ltBlank && lastSeg != nil {
199 | lastSeg.Code = append(lastSeg.Code, line)
200 | } else {
201 | lastSeg = &Seg{Code: []string{line}}
202 | segs = append(segs, lastSeg)
203 | }
204 | lastSeen = ltCode
205 | }
206 | }
207 | return
208 | }
209 |
210 | // Join concatenates the elements of its first argument to create a single string. The separator
211 | // string sep is placed between elements in the resulting string.
212 | func stringJoin(elems []string, sep string) []byte {
213 | switch len(elems) {
214 | case 0:
215 | return nil
216 | case 1:
217 | return []byte(elems[0])
218 | }
219 | n := len(sep) * (len(elems) - 1)
220 | for i := 0; i < len(elems); i++ {
221 | n += len(elems[i])
222 | }
223 |
224 | var b bytes.Buffer
225 | b.Grow(n)
226 | b.WriteString(elems[0])
227 | for _, s := range elems[1:] {
228 | b.WriteString(sep)
229 | b.WriteString(s)
230 | }
231 | return b.Bytes()
232 | }
233 |
234 | func markdown(docs []string) []byte {
235 | text := stringJoin(docs, "\n")
236 | return blackfriday.Run(text)
237 | }
238 |
239 | func parseAndRenderSegs(sourcePath string) []*Seg {
240 | filecontent := mustReadFile(sourcePath)
241 | segs := parseSegs(filecontent)
242 | for _, seg := range segs {
243 | if seg.Docs != nil {
244 | seg.DocsRendered = string(markdown(seg.Docs))
245 | }
246 | if seg.Code != nil {
247 | code := strings.Join(seg.Code, "\n")
248 | seg.CodeRendered = code
249 | }
250 | }
251 | return segs
252 | }
253 |
254 | // -----------------------------------------------------------------------------
255 |
256 | type exampleFile struct {
257 | // Lang is the language of code file, like `gop` / `go`
258 | Lang string
259 | // HeadDoc is document content before code content
260 | HeadDoc []*Seg
261 | // Code is all code segments of example file
262 | Code []*Seg
263 | // TailDoc is document content after code content
264 | TailDoc []*Seg
265 | }
266 |
267 | type example struct {
268 | *exampleIndex
269 | Files []*exampleFile
270 | }
271 |
272 | // classifySegs classify segments into headDoc, code & tailDoc
273 | func classifySegs(segs []*Seg) (headDoc, code, tailDoc []*Seg) {
274 | hasSeenCode := false
275 | for _, seg := range segs {
276 | if seg.Code != nil {
277 | code = append(code, seg)
278 | hasSeenCode = true
279 | continue
280 | }
281 | if hasSeenCode {
282 | // move all doc segs among code segs to tail,
283 | // so that single code block will be rendered as result
284 | tailDoc = append(tailDoc, seg)
285 | } else {
286 | headDoc = append(headDoc, seg)
287 | }
288 | }
289 | return
290 | }
291 |
292 | func langOf(fname string) string {
293 | i := strings.LastIndex(fname, ".")
294 | if i < 0 {
295 | return ""
296 | }
297 | return fname[i+1:]
298 | }
299 |
300 | func parseExample(dir string, idx *exampleIndex) *example {
301 | fis, err := os.ReadDir(dir)
302 | check(err)
303 | example := &example{exampleIndex: idx}
304 | for _, fi := range fis {
305 | fname := fi.Name()
306 | lang := langOf(fname)
307 | if lang != "xgo" && lang != "gop" { // only support XGo examples
308 | continue
309 | }
310 | sourcePath := filepath.Join(dir, fname)
311 | sourceSegs := parseAndRenderSegs(sourcePath)
312 | if len(sourceSegs) != 0 { // ignore file with no segs
313 | headDoc, code, tailDoc := classifySegs(sourceSegs)
314 | file := &exampleFile{lang, headDoc, code, tailDoc}
315 | example.Files = append(example.Files, file)
316 | }
317 | }
318 | return example
319 | }
320 |
321 | func renderExample(e *example) []byte {
322 | var buf bytes.Buffer
323 | err := exampleTmpl.Execute(&buf, e)
324 | check(err)
325 | return buf.Bytes()
326 | }
327 |
328 | func handleExample(w http.ResponseWriter, req *http.Request, root, path string) {
329 | if idx, ok := exampleIndexes[path]; ok {
330 | cache := atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&idx.cache)))
331 | if cache == nil {
332 | cache = unsafe.Pointer(parseExample(filepath.Join(root, idx.Name), idx))
333 | atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&idx.cache)), cache)
334 | }
335 | data := renderExample((*example)(cache))
336 | w.Write(data)
337 | return
338 | }
339 | http.ServeFile(w, req, "./public/404.html")
340 | }
341 |
342 | // -----------------------------------------------------------------------------
343 |
344 | func handle(root string) func(w http.ResponseWriter, req *http.Request) {
345 | var text []byte
346 | var wg sync.WaitGroup
347 | wg.Add(1)
348 | go func() {
349 | names, err := listTutorial(root)
350 | if err != nil {
351 | log.Panicln(err)
352 | }
353 | text = renderIndex(names)
354 | wg.Done()
355 |
356 | watcher, err = internal.NewWatcher(func(string) {
357 | for _, v := range exampleIndexes {
358 | atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&v.cache)), nil)
359 | }
360 | })
361 | if err != nil {
362 | log.Panicln(err)
363 | }
364 | if err := watcher.Watch(root); err != nil {
365 | log.Panicln(err)
366 | }
367 | }()
368 |
369 | return func(w http.ResponseWriter, req *http.Request) {
370 | urlPath := path.Clean(req.URL.Path)
371 | if !path.IsAbs(urlPath) {
372 | urlPath = "/404.html"
373 | }
374 | if urlPath == "/" {
375 | wg.Wait()
376 | w.Write(text)
377 | return
378 | }
379 | if path.Ext(urlPath) != "" {
380 | http.ServeFile(w, req, "./public"+urlPath)
381 | return
382 | }
383 | handleExample(w, req, root, urlPath)
384 | }
385 | }
386 |
387 | var (
388 | protocol = "http"
389 | address = "localhost:8000"
390 | host = flag.String("host", address, "Serving host")
391 | )
392 |
393 | func main() {
394 | flag.Parse()
395 | fmt.Printf("Serving XGo tutorial at %s://%s\n", protocol, *host)
396 | http.HandleFunc("/", handle("."))
397 | err := http.ListenAndServe(*host, nil)
398 | if err != nil {
399 | log.Fatalf("Failed to start server at %s://%s.\nError: %s.", protocol, *host, err)
400 | }
401 | }
402 |
403 | // -----------------------------------------------------------------------------
404 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | XGo by Tutorial: Not Found
6 |
7 |
8 |
9 |
10 |
21 | XGo is an open source programming language aimed to enable everyone to become a builder of the world.
22 |
23 |
24 | XGo by Tutorials is a hands-on introduction
25 | to XGo using annotated example programs. Check out
26 | the first example or
27 | browse the full list below.
28 |