├── .github ├── dependabot.yml └── workflows │ └── gop.yml ├── .gitignore ├── 100-Sequential-programming └── README.md ├── 101-Hello-world ├── hello-11.gop ├── hello-12.gop ├── hello-2.gop └── hello-3.gop ├── 102-Values └── values.gop ├── 103-Constants └── constants.gop ├── 104-Variables └── vars.gop ├── 105-Assignments ├── assign-1.gop └── assign-2.gop ├── 106-Types ├── types-2.gop └── types.gop ├── 107-Integers └── integers.gop ├── 108-Floating-Point-Numbers └── numbers.gop ├── 109-Complex-Numbers ├── complex-1.gop ├── complex-2.gop └── complex-3.gop ├── 110-Booleans ├── boolean-1.gop └── boolean-2.gop ├── 111-Strings └── strings.gop ├── 112-Rational-Numbers └── rational-1.gop ├── 113-If/Else └── if-else.gop ├── 113-Switch └── switch-1.gop ├── 114-For ├── for-0.gop ├── for-1.gop ├── for-2.gop ├── for-3.gop ├── for-4.gop ├── for-5.gop ├── for-6.gop └── for-7.gop ├── 116-Arrays ├── arrays1.gop ├── arrays2.gop └── arrays3.gop ├── 117-Slices ├── slices-01.gop ├── slices-02.gop ├── slices-03.gop ├── slices-04.gop ├── slices-05.gop ├── slices-06.gop ├── slices-07.gop ├── slices-08.gop ├── slices-09.gop ├── slices-10.gop ├── slices-11.gop └── slices-12.gop ├── 118-Maps ├── map-0.gop ├── map-1.gop └── map-2.gop ├── 119-Structs ├── struct-1.gop ├── struct-2.gop ├── struct-3.gop ├── struct-4.gop ├── struct-5.gop ├── struct-6.gop └── struct-7.gop ├── 120-Pointers ├── pointer-1.gop ├── pointer-2.gop ├── pointer-3.gop └── pointer-4.gop ├── 121-For-Each ├── for-each-1.gop ├── for-each-2.gop └── for-each-3.gop ├── 121-For-Range └── range.gop ├── 121-List-Comprehension └── listcompr.gop ├── 200-Structured programming └── README.md ├── 201-Functions └── funcs.gop ├── 202-Multiple-Return-Values └── multi-rets.gop ├── 203-Errors └── errors.gop ├── 204-Function Values └── func-values.gop ├── 205-Closures └── closures.gop ├── 205-Lambda-expressions ├── lambda-expressions-1.gop ├── lambda-expressions-2.gop └── lambda-expressions-3.gop ├── 206-Recursion └── recursion.gop ├── 207-Variadic-Parameters └── variadic.gop ├── 208-Defer ├── defer-1.gop └── defer-2.gop ├── 209-Exceptions ├── exceptions-1.gop └── exceptions-2.gop ├── 210-Methods ├── methods-1.gop └── methods-2.gop ├── 211-Methods-with-a-Pointer-Receiver └── ptr-methods-1.gop ├── 212-Composing-Types-by-Struct-Embedding ├── struct-emb-1.gop └── struct-emb-2.gop ├── 213-Method-Values-and-Expressions ├── method-values-1.gop ├── method-values-2.gop └── method-values-3.gop ├── 214-Encapsulation ├── encap-1.gop ├── encap-2.gop ├── encap-3.gop ├── encap-4.gop ├── encap-5.gop └── encap-6.gop ├── 215-Interfaces ├── interfaces-1.gop └── interfaces-2.gop ├── 216-Interface-Satisfaction ├── interface-satisfy-1.gop ├── interface-satisfy-2.gop ├── interface-satisfy-3.gop ├── interface-satisfy-4.gop ├── interface-satisfy-5.gop └── interface-satisfy-6.gop ├── 217-Interface-Values ├── interface-values-1.gop ├── interface-values-2.gop └── interface-values-3.gop ├── 218-The-error-Interface ├── error-1.gop ├── error-2.gop ├── error-3.gop └── error-4.gop ├── 219-Type-Assertions ├── type-assert-1.gop ├── type-assert-2.gop ├── type-assert-3.gop └── type-assert-4.gop ├── 220-Type-Switches └── type-switch.gop ├── LICENSE ├── README.md ├── _deep ├── Unix-Shebang │ └── shebang └── Using-goplus-in-Go │ └── foo │ ├── foo.gop │ ├── foo_test.gop │ └── footest_test.gop ├── _todo ├── 02-Var-and-operator │ └── var_and_op.gop ├── 03-Import-go-package │ └── import.gop ├── 04-Func │ └── func.gop ├── 05-Closure │ └── closure.gop ├── 06-String-Map-Array-Slice │ └── datastruct.gop ├── 08-SliceLit │ └── slicelit.gop ├── 09-IfElse-SwitchCase │ └── flow.gop ├── 10-List-comprehension │ └── list_comprehens.gop ├── 11-Map-comprehension │ └── map_comprehens.gop ├── 12-Select-comprehension │ └── select.gop ├── 12-Select-comprehension2 │ └── findscore.gop ├── 13-Exists-comprehension │ └── exists.gop ├── 15-ErrWrap │ └── err_wrap.gop ├── 16-Fib │ └── fib.gop ├── 17-Fibtc │ └── fibtc.gop ├── 18-Rational │ └── rational.gop ├── 19-IncDec │ └── inc_dec.gop ├── 21-Break-continue-goto │ └── flow.gop ├── 22-For-loop │ └── for.gop ├── 23-Defer │ └── defer.gop ├── 24-Goroutine │ └── goroutine.gop ├── 25-Struct │ └── struct.gop ├── 26-Method │ └── method.gop ├── 27-Func-Set │ └── func.gop ├── 28-Chan │ └── chan.gop ├── 29-CompareToNil │ └── ref.gop ├── 30-Recover │ └── recover.gop ├── 31-Builtin-Typecast │ └── builtin_and_typecast.gop ├── 32-Import-gop-package │ └── import_gop_pkg.gop ├── 33-Interface │ └── shape.gop ├── 34-Type-assert │ └── type_assert.gop ├── 35-Chan-select │ └── select.gop ├── 36-Auto-Property │ └── autoprop.gop ├── 37-Cmdline │ └── cmdline.gop ├── 38-Overload-operator │ └── overload_op.gop ├── 39-Lambda-expression │ └── lambda.gop ├── 40-Deduce-struct-type │ └── deduce.gop ├── 41-UDT-RangeForEach │ └── udt_range.gop ├── 42-UDT-RangeIterator │ └── udt_range_iter.gop └── 43-RangeExpr │ └── rangeexpr.gop ├── dummy └── dummy.go ├── go.mod ├── go.sum ├── internal └── watch.go ├── main.go ├── public ├── 404.html ├── clipboard.min.js ├── clipboard.svg ├── favicon.svg ├── play.svg └── site.css └── templates ├── example.tmpl ├── footer.tmpl ├── header.tmpl └── index.tmpl /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gomod" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/gop.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a goplus project 2 | name: Go+ 3 | 4 | on: 5 | push: 6 | branches: [ "main" ] 7 | pull_request: 8 | branches: [ "main" ] 9 | 10 | jobs: 11 | 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | 17 | - name: Set up Go/Go+ 18 | uses: goplus/setup-goplus@v1.1.0 19 | with: 20 | go-version: "1.18" 21 | gop-version: "main" 22 | 23 | - name: SingleFile Mode Test 24 | run: | 25 | gop go -ignore-notated-error -t -s ./... 26 | 27 | - name: Go+ Format Test 28 | run: | 29 | gop fmt -t ./... 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | *.txt 6 | *.cache 7 | .DS_Store 8 | go.json 9 | x.mod 10 | gop 11 | gopfmt 12 | goptestgo 13 | 14 | # Folders 15 | _obj 16 | _test 17 | _old 18 | _t 19 | 20 | # Architecture specific extensions/prefixes 21 | *.[568vq] 22 | [568vq].out 23 | 24 | *.cgo1.go 25 | *.cgo2.c 26 | _cgo_defun.c 27 | _cgo_gotypes.go 28 | _cgo_export.* 29 | 30 | _testmain.go 31 | coverage.txt 32 | 33 | .gop/ 34 | gop_autogen*.go 35 | gop_autogen*_test.go 36 | go.json 37 | *.cache 38 | 39 | *.exe 40 | *.test 41 | *.prof 42 | 43 | .vscode 44 | .idea 45 | go.work* 46 | 47 | cmd/qfmt/qfmt 48 | -------------------------------------------------------------------------------- /100-Sequential-programming/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goplus/tutorial/70c515cad9e678a688116060eaa91149dc4ca512/100-Sequential-programming/README.md -------------------------------------------------------------------------------- /101-Hello-world/hello-11.gop: -------------------------------------------------------------------------------- 1 | // There are three ways to write XGo's Hello world. 2 | 3 | # The first type: command style 4 | 5 | println "Hello world" 6 | 7 | // This is our recommended way of writing, and it is very easy to understand. 8 | // Especially for elementary and middle school students, commands are the easiest 9 | // logic for them to understand, which is much easier to understand than function calls. 10 | -------------------------------------------------------------------------------- /101-Hello-world/hello-12.gop: -------------------------------------------------------------------------------- 1 | // To emphasize our preference for the command style, we introduce `echo` as an alias 2 | // for `println`: 3 | 4 | echo "Hello world" 5 | -------------------------------------------------------------------------------- /101-Hello-world/hello-2.gop: -------------------------------------------------------------------------------- 1 | # The second type: function call style 2 | 3 | println("Hello world") 4 | 5 | // This way of writing is more like Python. The basis for understanding this kind of writing is 6 | // to understand what is a function call. This understanding is not too difficult, especially 7 | // if middle school students have learned functions (such as sin) in mathematics classes, it 8 | // is relatively easy to understand. Most programming languages ​​also support this standard 9 | // function call syntax. 10 | -------------------------------------------------------------------------------- /101-Hello-world/hello-3.gop: -------------------------------------------------------------------------------- 1 | # The third type: software engineering style 2 | 3 | package main 4 | 5 | func main() { 6 | println("Hello world") 7 | } 8 | 9 | // This is a standard software engineering-based writing method that has been inherited from 10 | // Go. It is not easy for beginners to understand, because they need to understand what is a 11 | // function (func) and what is a package (package). Of course, it also has its benefits, 12 | // starting to allow you to establish some basic logic for functional decomposition and team 13 | // collaboration. 14 | 15 | // So, how do you try to play XGo? 16 | 17 | // The simplest, go directly to XGo Playground to play: 18 | 19 | // * https://play.xgo.dev 20 | 21 | // When learning basic grammar in the early days, it can actually be done in this way. 22 | 23 | // How to install XGo to play locally? We will talk about this topic later. 24 | -------------------------------------------------------------------------------- /102-Values/values.gop: -------------------------------------------------------------------------------- 1 | // XGo has various value types including strings, 2 | // integers, floats, booleans, etc. 3 | 4 | // Here are a few basic examples. 5 | 6 | // Strings, which can be added together with `+`. 7 | println "X"+"Go" 8 | 9 | // Integers and floats. 10 | println "1+1 =", 1+1 11 | println "7.0/3.0 =", 7.0/3.0 12 | 13 | // Booleans, with boolean operators as you'd expect. 14 | println true && false 15 | println true || false 16 | println !true 17 | -------------------------------------------------------------------------------- /103-Constants/constants.gop: -------------------------------------------------------------------------------- 1 | // XGo supports _constants_ of character, string, boolean, 2 | // and numeric values. 3 | 4 | import ( 5 | "math" 6 | ) 7 | 8 | // `const` declares a constant value. 9 | const s string = "constant" 10 | 11 | println s 12 | 13 | // A `const` statement can appear anywhere a `var` 14 | // statement can. 15 | const n = 500000000 16 | 17 | // Constant expressions perform arithmetic with 18 | // arbitrary precision. 19 | const d = 3e20 / n 20 | println d 21 | 22 | // A numeric constant has no type until it's given 23 | // one, such as by an explicit conversion. 24 | println int64(d) 25 | 26 | // A number can be given a type by using it in a 27 | // context that requires one, such as a variable 28 | // assignment or function call. For example, here 29 | // `math.sin` expects a `float64`. 30 | println math.sin(n) 31 | -------------------------------------------------------------------------------- /104-Variables/vars.gop: -------------------------------------------------------------------------------- 1 | // In XGo, _variables_ are explicitly declared and used by 2 | // the compiler to e.g. check type-correctness of function 3 | // calls. 4 | 5 | // `var` declares 1 or more variables. 6 | var a = "initial" 7 | 8 | println a 9 | 10 | // You can declare multiple variables at once. 11 | var b, c int = 1, 2 12 | println b, c 13 | 14 | // XGo will infer the type of initialized variables. 15 | var d = true 16 | println d 17 | 18 | // Variables declared without a corresponding 19 | // initialization are _zero-valued_. For example, the 20 | // zero value for an `int` is `0`. 21 | var e int 22 | println e 23 | 24 | // The `:=` syntax is shorthand for declaring and 25 | // initializing a variable, e.g. for 26 | // `var f string = "apple"` in this case. 27 | f := "apple" 28 | println f 29 | -------------------------------------------------------------------------------- /105-Assignments/assign-1.gop: -------------------------------------------------------------------------------- 1 | # assignment 2 | 3 | // `=` is used for assigning. The values of multiple variables can be changed in one line. In this way, their values can be swapped without an intermediary variable. 4 | var age int 5 | 6 | age = 21 // = is used for assigning. 7 | println age 8 | 9 | var a, b int = 0, 1 10 | a, b = b, a // The values of multiple variables can be changed in one line. 11 | println a, b // 1, 0 12 | -------------------------------------------------------------------------------- /105-Assignments/assign-2.gop: -------------------------------------------------------------------------------- 1 | # `=` vs `:=` 2 | 3 | // := is used for declaring and initializing. Multiple variables can be declared and intialized at one line. 4 | age := 21 // age is declared and initialized to 21 5 | println age 6 | 7 | a, b := 0, 1 // multiple variables can be declared and intialized at one line. 8 | a, b = b, a 9 | println a, b // 1, 0 10 | -------------------------------------------------------------------------------- /106-Types/types-2.gop: -------------------------------------------------------------------------------- 1 | # XGo has built-in support for rational numbers: 2 | // 3 | // 4 | // We introduce rational numbers as primitive XGo types. We use suffix r to denote rational literals. For example, 1r << 200 means a big int whose value is equal to 2^200. 5 | // 6 | // By default, 1r will have the type of bigint. 7 | // And 4/5r means the rational constant 4/5. It will have the type of bigrat. 8 | a := 1r << 200 9 | b := bigint(1 << 200) 10 | c := 4/5r 11 | d := bigrat(a) - 1/3r + 3*1/2r 12 | println a, b, c, d 13 | 14 | r1 := 1r 15 | br := bigrat(1r) // Casting rational numbers works like other primitive types 16 | cr := bigrat(1) // Casting normal numbers also works like other primitive types 17 | println r1/3 // 0 18 | println br/3 // 1/3 19 | println cr/3 // 1/3 20 | -------------------------------------------------------------------------------- /106-Types/types.gop: -------------------------------------------------------------------------------- 1 | # XGo primitive types are 2 | //
 3 | // bool
 4 | // int8    int16   int32   int    int64    int128
 5 | // uint8   uint16  uint32  uint   uint64   uint128
 6 | // uintptr (similar to C's size_t)
 7 | // byte (alias for uint8)
 8 | // rune (alias for int32, represents a Unicode code point)
 9 | // string
10 | // float32 float64
11 | // complex64 complex128
12 | // bigint bigrat
13 | // unsafe.Pointer (similar to C's void*)
14 | // any (alias for Go's interface{})
15 | // 
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 | //
 9 | //   \n:line break
10 | //   \r:Enter
11 | //   \t:Tab
12 | //   \u or \U:Unicode
13 | //   \:Backslash
14 | // 
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 | //
 7 | //a := make([]int, 5)			// len(a)=5
 8 | //To specify a capacity, pass a third argument to make:
 9 | //b := make([]int, 0, 5) 		// len(b)=0, cap(b)=5
10 | //b = b[:cap(b)] 				// len(b)=5, cap(b)=5
11 | //b = b[1:]      				// len(b)=4, cap(b)=4
12 | //
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 | [![Build Status](https://github.com/goplus/tutorial/actions/workflows/gop.yml/badge.svg)](https://github.com/goplus/tutorial/actions/workflows/gop.yml) 4 | [![Tutorials](https://img.shields.io/badge/tutorial-XGo-blue.svg)](https://tutorial.xgo.dev) 5 | [![Playground](https://img.shields.io/badge/playground-XGo-blue.svg)](https://play.xgo.dev) 6 | [![Language](https://img.shields.io/badge/language-XGo-blue.svg)](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 |
11 |

XGo by Tutorial

12 |

Sorry, we couldn't find that! Check out the home page?

13 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /public/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.8 3 | * https://clipboardjs.com/ 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/site.css: -------------------------------------------------------------------------------- 1 | /* CSS reset: http://meyerweb.com/eric/tools/css/reset/ */ 2 | /* 3 | :root { 4 | --layout-width: 1000px; 5 | --layout-doc-width: 420px; 6 | --layout-code-width: 580px; 7 | } */ 8 | 9 | html, body, div, span, applet, object, iframe, 10 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 11 | a, abbr, acronym, address, big, cite, code, 12 | del, dfn, em, img, ins, kbd, q, s, samp, 13 | small, strike, strong, sub, sup, tt, var, 14 | b, u, i, center, 15 | dl, dt, dd, ol, ul, li, 16 | fieldset, form, label, legend, 17 | table, caption, tbody, tfoot, thead, tr, th, td, 18 | article, aside, canvas, details, embed, 19 | figure, figcaption, footer, header, hgroup, 20 | menu, nav, output, ruby, section, summary, 21 | time, mark, audio, video { 22 | margin: 0; 23 | padding: 0; 24 | border: 0; 25 | font-size: 100%; 26 | font: inherit; 27 | vertical-align: baseline; 28 | } 29 | article, aside, details, figcaption, figure, 30 | footer, header, hgroup, menu, nav, section { 31 | display: block; 32 | } 33 | body { 34 | line-height: 1; 35 | } 36 | ol, ul { 37 | list-style: none; 38 | } 39 | blockquote, q { 40 | quotes: none; 41 | } 42 | blockquote:before, blockquote:after, 43 | q:before, q:after { 44 | content: ''; 45 | content: none; 46 | } 47 | table { 48 | border-collapse: collapse; 49 | border-spacing: 0; 50 | } 51 | 52 | /* Layout and typography */ 53 | body { 54 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji'; 55 | font-size: 14px; 56 | line-height: 1.5715; 57 | color: #333; 58 | } 59 | 60 | * { 61 | box-sizing: border-box; 62 | } 63 | 64 | /* main wrapper */ 65 | .main { 66 | width: 1176px; 67 | position: relative; 68 | margin: 0 auto; 69 | } 70 | @media screen and (min-width: 641px) { 71 | .main { 72 | margin-bottom: 76px; 73 | } 74 | } 75 | @media screen and (max-width: 640px) { 76 | .main { 77 | width: 100%; 78 | padding: 16px; 79 | } 80 | } 81 | 82 | /* breadcrumb */ 83 | .breadcrumb { 84 | display: flex; 85 | flex-direction: row; 86 | margin-top: 36px; 87 | font-size: 16px; 88 | line-height: 1.5; 89 | } 90 | @media screen and (max-width: 640px) { 91 | .breadcrumb { 92 | margin-top: 0; 93 | } 94 | } 95 | .breadcrumb-link-item, .breadcrumb-sep { 96 | color: #999999; 97 | } 98 | .breadcrumb-link-item::after { 99 | content: "/"; 100 | margin: 0 8px; 101 | } 102 | .breadcrumb-link-item a { 103 | color: inherit; 104 | text-decoration: none; 105 | transition: all .3s; 106 | } 107 | .breadcrumb-link-item a:hover { 108 | color: #333; 109 | } 110 | .breadcrumb-sep { 111 | margin: 0 8px; 112 | } 113 | .breadcrumb-current-item { 114 | color: #333; 115 | } 116 | 117 | /* text wrapper */ 118 | .text-wrapper { 119 | font-size: 16px; 120 | line-height: 1.5; 121 | } 122 | .text-wrapper em { 123 | font-style: italic; 124 | } 125 | .text-wrapper a { 126 | text-decoration: none; 127 | } 128 | .text-wrapper a, .text-wrapper a:visited { 129 | color: #2C84FF; 130 | } 131 | .text-wrapper a:hover { 132 | text-decoration: underline; 133 | } 134 | .text-wrapper h1, .text-wrapper h2, .text-wrapper h3, .text-wrapper h4, .text-wrapper h5, .text-wrapper h6 { 135 | font-weight: 500; 136 | font-family: PingFangSC, PingFangSC-Medium, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji'; 137 | } 138 | .text-wrapper h2 { 139 | font-size: 32px; 140 | line-height: 45px; 141 | margin: 28px 0 40px; 142 | } 143 | .text-wrapper h3 { 144 | font-size: 24px; 145 | line-height: 1.5; 146 | margin: 28px 0 24px; 147 | } 148 | .text-wrapper h4 { 149 | font-size: 16px; 150 | margin: 24px 0 20px; 151 | } 152 | .text-wrapper h5 { 153 | font-size: 14px; 154 | margin: 20px 0 16px; 155 | } 156 | .text-wrapper p { 157 | margin: 22px 0; 158 | } 159 | .text-wrapper ul, .text-wrapper ol { 160 | margin-bottom: 22px; 161 | padding-left: 2em; 162 | } 163 | .text-wrapper ul { 164 | list-style: initial; 165 | } 166 | .text-wrapper ol { 167 | list-style: decimal; 168 | } 169 | .text-wrapper li { 170 | margin-bottom: 12px; 171 | } 172 | .text-wrapper li::marker { 173 | color: #999; 174 | } 175 | 176 | /* example link list in home page */ 177 | @media screen and (min-width: 641px) { 178 | h3.example-link-title { 179 | margin-top: 56px; 180 | } 181 | .example-link-list + h3.example-link-title { 182 | margin-top: 44px; 183 | } 184 | .text-wrapper .example-link-list { 185 | column-count: 2; 186 | } 187 | } 188 | /* See goplus/www/goplus.org/utils/index.scss */ 189 | .text-wrapper pre, .text-wrapper code { 190 | padding: 0.2em 0.4em; 191 | margin: 0; 192 | font-size: 85%; 193 | border-radius: 6px; 194 | font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; 195 | } 196 | 197 | .text-wrapper code { 198 | background-color: rgba(175, 184, 193, 0.2); 199 | } 200 | 201 | /* next example link in example page */ 202 | p.next { 203 | margin: 72px 0; 204 | padding: 16px 0; 205 | border-top: 1px solid #E5E5E5; 206 | } 207 | p.next a::after { 208 | content: " >"; 209 | } 210 | @media screen and (max-width: 640px) { 211 | p.next { 212 | margin: 36px 0 0; 213 | } 214 | } 215 | 216 | /* docs in example page */ 217 | .docs { 218 | width: 50%; 219 | } 220 | @media screen and (max-width: 640px) { 221 | .docs { 222 | width: 100%; 223 | } 224 | } 225 | 226 | /* Syntax highlighting */ 227 | body .hll { background-color: #ffffcc } 228 | body .err { border: 1px solid #FF0000 } /* Error */ 229 | body .c { color: #408080; font-style: italic } /* Comment */ 230 | body .k { color: #954121 } /* Keyword */ 231 | body .o { color: #666666 } /* Operator */ 232 | body .cm { color: #408080; font-style: italic } /* Comment.Multiline */ 233 | body .cp { color: #BC7A00 } /* Comment.Preproc */ 234 | body .c1 { color: #408080; font-style: italic } /* Comment.Single */ 235 | body .cs { color: #408080; font-style: italic } /* Comment.Special */ 236 | body .gd { color: #A00000 } /* Generic.Deleted */ 237 | body .ge { font-style: italic } /* Generic.Emph */ 238 | body .gr { color: #FF0000 } /* Generic.Error */ 239 | body .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 240 | body .gi { color: #00A000 } /* Generic.Inserted */ 241 | body .go { color: #808080 } /* Generic.Output */ 242 | body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 243 | body .gs { font-weight: bold } /* Generic.Strong */ 244 | body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 245 | body .gt { color: #0040D0 } /* Generic.Traceback */ 246 | body .kc { color: #954121 } /* Keyword.Constant */ 247 | body .kd { color: #954121 } /* Keyword.Declaration */ 248 | body .kn { color: #954121 } /* Keyword.Namespace */ 249 | body .kp { color: #954121 } /* Keyword.Pseudo */ 250 | body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */ 251 | body .kt { color: #B00040 } /* Keyword.Type */ 252 | body .m { color: #666666 } /* Literal.Number */ 253 | body .s { color: #219161 } /* Literal.String */ 254 | body .na { color: #7D9029 } /* Name.Attribute */ 255 | body .nb { color: #954121 } /* Name.Builtin */ 256 | body .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 257 | body .no { color: #880000 } /* Name.Constant */ 258 | body .nd { color: #AA22FF } /* Name.Decorator */ 259 | body .ni { color: #999999; font-weight: bold } /* Name.Entity */ 260 | body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 261 | body .nf { } /* Name.Function */ 262 | body .nl { color: #A0A000 } /* Name.Label */ 263 | body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 264 | body .nt { color: #954121; font-weight: bold } /* Name.Tag */ 265 | body .nv { color: #19469D } /* Name.Variable */ 266 | body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 267 | body .w { color: #bbbbbb } /* Text.Whitespace */ 268 | body .mf { color: #666666 } /* Literal.Number.Float */ 269 | body .mh { color: #666666 } /* Literal.Number.Hex */ 270 | body .mi { color: #666666 } /* Literal.Number.Integer */ 271 | body .mo { color: #666666 } /* Literal.Number.Oct */ 272 | body .sb { color: #219161 } /* Literal.String.Backtick */ 273 | body .sc { color: #219161 } /* Literal.String.Char */ 274 | body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */ 275 | body .s2 { color: #219161 } /* Literal.String.Double */ 276 | body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 277 | body .sh { color: #219161 } /* Literal.String.Heredoc */ 278 | body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 279 | body .sx { color: #954121 } /* Literal.String.Other */ 280 | body .sr { color: #BB6688 } /* Literal.String.Regex */ 281 | body .s1 { color: #219161 } /* Literal.String.Single */ 282 | body .ss { color: #19469D } /* Literal.String.Symbol */ 283 | body .bp { color: #954121 } /* Name.Builtin.Pseudo */ 284 | body .vc { color: #19469D } /* Name.Variable.Class */ 285 | body .vg { color: #19469D } /* Name.Variable.Global */ 286 | body .vi { color: #19469D } /* Name.Variable.Instance */ 287 | body .il { color: #666666 } /* Literal.Number.Integer.Long */ 288 | -------------------------------------------------------------------------------- /templates/example.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | XGo by Tutorials: {{.Title}} 6 | 7 | 8 | 9 | 10 | 11 | 21 | 22 | {{ template "header" }} 23 |
24 | 29 |
30 |

{{.Title}}

31 | {{if .Files}}{{else}} 32 |

No content yet, you can help us build it here.

33 | {{end}} 34 | {{range .Files}} 35 | {{if .HeadDoc}} 36 |
37 | {{range .HeadDoc}} 38 | {{.DocsRendered}} 39 | {{end}} 40 |
41 | {{end}} 42 | {{if .Code}} 43 | {{range .Code}}{{if .DocsRendered}}{{.DocsRendered}}{{end}}{{.CodeRendered}}{{end}} 44 | {{end}} 45 | {{if .TailDoc}} 46 |
47 | {{range .TailDoc}} 48 | {{.DocsRendered}} 49 | {{end}} 50 |
51 | {{end}} 52 | {{end}} 53 | {{if .Next}} 54 |

55 | Next example: {{.Next.Title}} 56 |

57 | {{end}} 58 |
59 |
60 | {{ template "footer" }} 61 | 62 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /templates/footer.tmpl: -------------------------------------------------------------------------------- 1 | {{define "footer"}} 2 | 3 | {{end}} 4 | -------------------------------------------------------------------------------- /templates/header.tmpl: -------------------------------------------------------------------------------- 1 | {{define "header"}} 2 | 3 |
4 |
5 | {{end}} 6 | -------------------------------------------------------------------------------- /templates/index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | XGo by Tutorials 6 | 7 | 8 | 9 | 10 | 11 | 12 | {{ template "header" }} 13 |
14 | 18 |
19 |

Tutorials

20 |

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 |

29 | {{range .}} 30 | {{if .Title}}{{end}} 31 | 36 | {{end}} 37 |
38 |
39 | {{ template "footer" }} 40 | 41 | 42 | --------------------------------------------------------------------------------