diff --git a/.github/workflows/docs_ci.yml b/.github/workflows/docs_ci.yml index 98267dfa6d..555ece0179 100644 --- a/.github/workflows/docs_ci.yml +++ b/.github/workflows/docs_ci.yml @@ -19,5 +19,5 @@ jobs: - uses: actions/checkout@v2 - name: Build V run: make - - name: Check docs line length + - name: Check docs line length & code examples run: ./v run cmd/tools/check-md.v doc/docs.md doc/upcoming.md CHANGELOG.md diff --git a/cmd/tools/check-md.v b/cmd/tools/check-md.v index a8ba9888b3..06ea6aab4a 100644 --- a/cmd/tools/check-md.v +++ b/cmd/tools/check-md.v @@ -1,15 +1,21 @@ module main import os +import rand +import term +import v.pref const ( too_long_line_length = 100 + term_colors = term.can_show_color_on_stderr() ) fn main() { files_paths := os.args[1..] mut warnings := 0 mut errors := 0 + mut oks := 0 + mut all_md_files := []MDFile{} for file_path in files_paths { real_path := os.real_path(file_path) lines := os.read_lines(real_path) or { @@ -17,27 +23,207 @@ fn main() { warnings++ continue } + mut mdfile := MDFile{ + path: file_path + } for i, line in lines { if line.len > too_long_line_length { - linetrace_msg := '$file_path:${i + 1}:${line.len + 1}: ' if line.starts_with('|') { - println(linetrace_msg + 'long table (warn)') + println(wline(file_path, i, line.len, 'long table')) warnings++ } else if line.contains('https') { - println(linetrace_msg + 'long link (warn)') + println(wline(file_path, i, line.len, 'long link')) warnings++ } else { - eprintln(linetrace_msg + 'line too long') + eprintln(eline(file_path, i, line.len, 'line too long')) errors++ } } + mdfile.parse_line(i, line) } + all_md_files << mdfile } - if warnings > 0 || errors > 0 { - println('\nWarnings | Errors') - println('$warnings\t | $errors') + for mut mdfile in all_md_files { + new_errors, new_oks := mdfile.check_examples() + errors += new_errors + oks += new_oks + } + // println('all_md_files: $all_md_files') + if warnings > 0 || errors > 0 || oks > 0 { + println('\nWarnings: $warnings | Errors: $errors | OKs: $oks') } if errors > 0 { exit(1) } } + +fn ftext(s string, cb fn (string) string) string { + if term_colors { + return cb(s) + } + return s +} + +fn btext(s string) string { + return ftext(s, term.bold) +} + +fn mtext(s string) string { + return ftext(s, term.magenta) +} + +fn rtext(s string) string { + return ftext(s, term.red) +} + +fn wline(file_path string, lnumber int, column int, message string) string { + return btext('$file_path:${lnumber + 1}:${column + 1}:') + btext(mtext(' warn:')) + rtext(' $message') +} + +fn eline(file_path string, lnumber int, column int, message string) string { + return btext('$file_path:${lnumber + 1}:${column + 1}:') + btext(rtext(' error: $message')) +} + +// +const ( + default_command = 'compile' +) + +struct VCodeExample { +mut: + text []string + command string + sline int + eline int +} + +enum MDFileParserState { + markdown + vexample +} + +struct MDFile { + path string +mut: + examples []VCodeExample + current VCodeExample + state MDFileParserState = .markdown +} + +fn (mut f MDFile) parse_line(lnumber int, line string) { + if line.starts_with('```v') { + if f.state == .markdown { + f.state = .vexample + mut command := line.replace('```v', '').trim_space() + if command == '' { + command = default_command + } + f.current = VCodeExample{ + sline: lnumber + command: command + } + } + return + } + if line.starts_with('```') && f.state == .vexample { + f.state = .markdown + f.current.eline = lnumber + f.examples << f.current + f.current = VCodeExample{} + return + } + if f.state == .vexample { + f.current.text << line + } +} + +fn (mut f MDFile) dump() { + for e in f.examples { + eprintln('f.path: $f.path | example: $e') + } +} + +fn (mut f MDFile) check_examples() (int, int) { + mut errors := 0 + mut oks := 0 + vexe := pref.vexe_path() + for e in f.examples { + if e.command == 'ignore' { + continue + } + if e.command == 'wip' { + continue + } + fname := os.base(f.path).replace('.md', '_md') + uid := rand.ulid() + vfile := os.join_path(os.temp_dir(), 'check_${fname}_example_${e.sline}__${e.eline}__${uid}.v') + mut should_cleanup_vfile := true + // eprintln('>>> checking example $vfile ...') + vcontent := e.text.join('\n') + os.write_file(vfile, vcontent) or { + panic(err) + } + mut acommands := e.command.split(' ') + for command in acommands { + match command { + 'compile' { + res := os.system('"$vexe" -silent -o x.c $vfile') + os.rm('x.c') or { } + if res != 0 { + eprintln(eline(f.path, e.sline, 0, 'example failed to compile')) + eprintln(vcontent) + should_cleanup_vfile = false + errors++ + continue + } + oks++ + } + 'failcompile' { + res := os.system('"$vexe" -silent -o x.c $vfile') + os.rm('x.c') or { } + if res == 0 { + eprintln(eline(f.path, e.sline, 0, '`failcompile` example compiled')) + eprintln(vcontent) + should_cleanup_vfile = false + errors++ + continue + } + oks++ + } + 'oksyntax' { + res := os.system('"$vexe" -silent -check-syntax $vfile') + if res != 0 { + eprintln(eline(f.path, e.sline, 0, '`oksyntax` example with invalid syntax')) + eprintln(vcontent) + should_cleanup_vfile = false + errors++ + continue + } + oks++ + } + 'badsyntax' { + res := os.system('"$vexe" -silent -check-syntax $vfile') + if res == 0 { + eprintln(eline(f.path, e.sline, 0, '`badsyntax` example can be parsed fine')) + eprintln(vcontent) + should_cleanup_vfile = false + errors++ + continue + } + oks++ + } + else { + eprintln(eline(f.path, e.sline, 0, 'unrecognized command: "$command", use one of: wip/ignore/compile/failcompile/oksyntax/badsyntax')) + should_cleanup_vfile = false + errors++ + } + } + } + if should_cleanup_vfile { + os.rm(vfile) or { + panic(err) + } + } + } + return errors, oks +} diff --git a/doc/docs.md b/doc/docs.md index ab08a8458e..2fe45841e5 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -96,10 +96,20 @@ Anything you can do in other languages, you can do in V. - + ## Hello World + ```v fn main() { println('hello world') @@ -265,7 +275,7 @@ Try compiling the program above after removing `mut` from the first line. Note the (important) difference between `:=` and `=`. `:=` is used for declaring and initializing, `=` is used for assigning. -```v +```v failcompile fn main() { age = 21 } @@ -304,7 +314,7 @@ that is already used in a parent scope will cause a compilation error. ### Primitive types -```v +```v ignore bool string @@ -331,7 +341,7 @@ on one side can be automatically promoted if it fits completely into the data range of the type on the other side. These are the allowed possibilities: -``` +```v ignore i8 → i16 → int → i64 ↘ ↘ f32 → f64 @@ -360,7 +370,7 @@ assert windows_newline.len == 2 In V, a string is a read-only array of bytes. String data is encoded using UTF-8. String values are immutable. You cannot mutate elements: -```v +```v failcompile mut s := 'hello 🌎' s[0] = `H` // not allowed ``` @@ -411,6 +421,7 @@ println('[${int(x):-10}]') // pad with spaces on the right ### String operators ```v +name := 'Bob' bobby := name + 'by' // + is used to concatenate strings println(bobby) // "Bobby" @@ -421,7 +432,7 @@ println(s) // "hello world" All operators in V must have values of the same type on both sides. You cannot concatenate an integer to a string: -```v +```v failcompile age := 10 println('age = ' + age) // not allowed ``` @@ -430,12 +441,14 @@ println('age = ' + age) // not allowed We have to either convert `age` to a `string`: ```v +age := 11 println('age = ' + age.str()) ``` or use string interpolation (preferred): ```v +age := 12 println('age = $age') ``` @@ -688,7 +701,7 @@ println('Your OS is ${os}.') Any imported module name can be aliased using the `as` keyword: NOTE: this example will not compile unless you have created `mymod/sha256.v` -```v +```v failcompile import crypto.sha256 import mymod.sha256 as mysha256 @@ -712,7 +725,7 @@ fn (mut t MyTime) century() int { } fn main() { - my_time := MyTime{ + mut my_time := MyTime{ year: 2020, month: 12, day: 25 @@ -775,6 +788,11 @@ if x is Abc { If you have a struct field which should be checked, there is also a way to name an alias. ```v +struct MyStruct {x int} +struct MyStruct2 {y string} +type MySumType = MyStruct | MyStruct2 +struct Abc { bar MySumType } +x := Abc{ bar: MyStruct{123} } if x.bar is MyStruct as bar { // x.bar cannot be cast automatically // you must explicitly state "as bar" to create a variable with the MyStruct type @@ -797,13 +815,16 @@ println('one' in m) // true It's also useful for writing boolean expressions that are clearer and more compact: ```v +enum Token { plus minus div mult } +struct Parser { token Token } +parser := Parser{} if parser.token == .plus || parser.token == .minus || parser.token == .div || parser.token == .mult { - ... + // ... } if parser.token in [.plus, .minus, .div, .mult] { - ... + // ... } ``` @@ -998,15 +1019,18 @@ A defer statement defers the execution of a block of statements until the surrounding function returns. ```v +import os + fn read_log() { - f := os.open('log.txt') or { panic(err) } + mut ok := false + mut f := os.open('log.txt') or { panic(err) } defer { f.close() } - ... + // ... if !ok { // defer statement will be called here, the file will be closed return } - ... + // ... // defer statement will be called here, the file will be closed } ``` @@ -1037,6 +1061,10 @@ Structs are allocated on the stack. To allocate a struct on the heap and get a reference to it, use the `&` prefix: ```v +struct Point { + x int + y int +} p := &Point{10, 10} // References have the same syntax for accessing fields println(p.x) @@ -1049,7 +1077,7 @@ References are similar to Go pointers and C++ references. V doesn't allow subclassing, but it supports embedded structs: -```v +```v wip // TODO: this will be implemented later struct Button { Widget @@ -1085,6 +1113,10 @@ It's also possible to define custom default values. ### Short struct literal syntax ```v +struct Point{ + x int + y int +} mut p := Point{x: 10, y: 20} // you can omit the struct name when it's already known @@ -1108,11 +1140,17 @@ struct ButtonConfig { height int = 20 } +struct Button { + text string + width int + height int +} + fn new_button(c ButtonConfig) &Button { return &Button{ width: c.width - height: c.height - text: c.text + height: c.height + text: c.text } } @@ -1123,7 +1161,7 @@ assert button.height == 20 As you can see, both the struct name and braces can be omitted, instead of: -```v +```v ignore new_button(ButtonConfig{text:'Click me', width:100}) ``` @@ -1153,7 +1191,7 @@ __global: For example, here's the `string` type defined in the `builtin` module: -```v +```v ignore struct string { str byteptr pub: @@ -1164,7 +1202,7 @@ pub: It's easy to see from this definition that `string` is an immutable type. The byte pointer with the string data is not accessible outside `builtin` at all. The `len` field is public, but immutable: -```v +```v failcompile fn main() { str := 'hello' len := str.len // OK @@ -1222,6 +1260,7 @@ It is possible to modify function arguments by using the keyword `mut`: ```v struct User { + name string mut: is_registered bool } @@ -1267,11 +1306,14 @@ instead of `register(mut user)`. V makes it easy to return a modified version of an object: ```v +struct User{ name string age int is_registered bool } fn register(u User) User { return { u | is_registered: true } } +mut user := User{name: 'abc' age: 23} user = register(user) +println(user) ``` ### Anonymous & high order functions @@ -1304,12 +1346,13 @@ fn main() { ## References ```v +struct Foo{} fn (foo Foo) bar_method() { - ... + // ... } fn bar_function(foo Foo) { - ... + // ... } ``` @@ -1324,6 +1367,8 @@ You can ensure that the struct is always passed by reference by adding `&`: ```v +struct Foo{ abc int } + fn (foo &Foo) bar() { println(foo.abc) } @@ -1333,9 +1378,9 @@ fn (foo &Foo) bar() { `(mut foo Foo)` must be used. In general, V's references are similar to Go pointers and C++ references. -For example, a tree structure definition would look like this: +For example, a generic tree structure definition would look like this: -```v +```v wip struct Node { val T left &Node @@ -1394,9 +1439,7 @@ They can represent complex structures, and this is used quite often since there are no globals: --> -```v -println('Top cities: $TOP_CITIES.filter(.usa)') -vs +```v ignore println('Top cities: $top_cities.filter(.usa)') ``` @@ -1406,6 +1449,7 @@ println('Top cities: $top_cities.filter(.usa)') strings, numbers, arrays, maps, structs. ```v +struct User{ name string age int } println(1) // "1" println('hi') // "hi" println([1,2,3]) // "[1, 2, 3]" @@ -1433,7 +1477,7 @@ If you don't want to print a newline, use `print()` instead. The number of builtin functions is low. Other builtin functions are: -``` +```v ignore fn exit(exit_code int) fn panic(message string) fn print_backtrace() @@ -1449,12 +1493,12 @@ quite easy to do. To create a new module, create a directory with your module's name containing .v files with code: -``` +```shell cd ~/code/modules mkdir mymodule vim mymodule/myfile.v ``` -```v +```v failcompile // myfile.v module mymodule @@ -1466,7 +1510,7 @@ pub fn say_hi() { You can now use `mymodule` in your code: -```v +```v failcompile import mymodule fn main() { @@ -1574,14 +1618,19 @@ struct Venus {} type World = Moon | Mars | Venus sum := World(Moon{}) +println(sum) ``` To check whether a sum type instance holds a certain type, use `sum is Type`. To cast a sum type to one of its variants you can use `sum as Type`: ```v -fn (m Mars) dust_storm() bool +struct Moon {} +struct Mars {} +struct Venus {} +type World = Moon | Mars | Venus +fn (m Mars) dust_storm() bool { return true } fn main() { mut w := World(Moon{}) assert w is Moon @@ -1600,8 +1649,11 @@ fn main() { You can also use `match` to determine the variant: ```v -fn open_parachutes(n int) - +struct Moon {} +struct Mars {} +struct Venus {} +type World = Moon | Mars | Venus +fn open_parachutes(n int) { println(n) } fn land(w World) { match w { Moon {} // no atmosphere @@ -1624,9 +1676,15 @@ There are two ways to access the cast variant inside a match branch: - using `as` to specify a variable name ```v -fn (m Moon) moon_walk() -fn (m Mars) shiver() -fn (v Venus) sweat() +struct Moon {} +struct Mars {} +struct Venus {} + +type World = Moon | Mars | Venus + +fn (m Moon) moon_walk() {} +fn (m Mars) shiver() {} +fn (v Venus) sweat() {} fn pass_time(w World) { match w { @@ -1700,7 +1758,7 @@ Unlike other languages, V does not handle exceptions with `throw/try/catch` bloc `err` is defined inside an `or` block and is set to the string message passed to the `error()` function. `err` is empty if `none` was returned. -```v +```v oksyntax user := repo.find_user_by_id(7) or { println(err) // "User 7 not found" return @@ -1730,7 +1788,7 @@ any further. The body of `f` is essentially a condensed version of: -```v +```v ignore resp := http.get(url) or { return error(err) } @@ -1740,7 +1798,7 @@ The body of `f` is essentially a condensed version of: --- The second method is to break from execution early: -```v +```v oksyntax user := repo.find_user_by_id(7) or { return } @@ -1767,13 +1825,16 @@ fn do_something(s string) ?string { a := do_something('foo') or { 'default' } // a will be 'foo' b := do_something('bar') or { 'default' } // b will be 'default' +println(a) +println(b) ``` --- The fourth method is to use `if` unwrapping: ```v -if resp := http.get(url) { +import net.http +if resp := http.get('https://google.com') { println(resp.text) // resp is a http.Response, not an optional } else { println(err) @@ -1784,7 +1845,8 @@ Above, `http.get` returns a `?http.Response`. `resp` is only in scope for the fi ## Generics -```v +```v wip + struct Repo { db DB } @@ -1897,13 +1959,13 @@ variables: ```v fn f(ch chan int) { - ... + // ... } fn main() { - ... + ch := chan int{} go f(ch) - ... + // ... } ``` @@ -1911,6 +1973,8 @@ Objects can be pushed to channels using the arrow operator. The same operator ca pop objects from the other end: ```v +mut ch := chan int{} +mut ch2 := chan f64{} n := 5 x := 7.3 ch <- n // push @@ -1927,9 +1991,12 @@ to do so will then result in a runtime panic (with the exception of `select` and associated channel has been closed and the buffer is empty. This situation can be handled using an or branch (see [Handling Optionals](#handling-optionals)). -```v +```v wip +mut ch := chan int{} +mut ch2 := chan f64{} +// ... ch.close() -... +// ... m := <-ch or { println('channel has been closed') } @@ -1943,8 +2010,16 @@ y := <-ch2 ? The `select` command allows monitoring several channels at the same time without noticeable CPU load. It consists of a list of possible transfers and associated branches of statements - similar to the [match](#match) command: -```v -select { +```v wip +import time +fn main () { + mut c := chan f64{} + mut ch := chan f64{} + mut ch2 := chan f64{} + mut ch3 := chan f64{} + mut b := 0.0 + // ... + select { a := <-ch { // do something with `a` } @@ -1957,7 +2032,8 @@ select { > 500 * time.millisecond { // do something if no channel has become ready within 0.5s } -} + } +} ``` The timeout branch is optional. If it is absent `select` waits for an unlimited amount of time. @@ -1966,10 +2042,10 @@ by adding an `else { ... }` branch. `else` and `> timeout` are mutually exclusiv The `select` command can be used as an *expression* of type `bool` that becomes `false` if all channels are closed: -```v +```v wip if select { ch <- a { - ... + // ... } } { // channel was open @@ -1982,10 +2058,21 @@ if select { For special purposes there are some builtin properties and methods: ```v +struct Abc{x int} + +a := 2.13 +mut ch := chan f64{} + res := ch.try_push(a) // try to perform `ch <- a` -res2 := ch2.try_pop(mut b) // try to perform `b = <-ch2 +println(res) l := ch.len // number of elements in queue c := ch.cap // maximum queue length +println(l) +println(c) + +// mut b := Abc{} +// mut ch2 := chan f64{} +// res2 := ch2.try_pop(mut b) // try to perform `b = <-ch2 ``` The `try_push/pop()` methods will return immediately with one of the results `.success`, `.not_ready` or `.closed` - dependent on whether the object has been transferred or @@ -2007,12 +2094,12 @@ mut: } fn (mut b St) g() { - ... + b.mtx.m_lock() // read/modify/write b.x - ... + b.mtx.unlock() - ... + } fn caller() { @@ -2021,12 +2108,12 @@ fn caller() { mtx: sync.new_mutex() } go a.g() - ... + a.mtx.m_lock() // read/modify/write a.x - ... + a.mtx.unlock() - ... + } ``` @@ -2079,9 +2166,10 @@ No runtime reflection is used. This results in much better performance. ### Asserts ```v -mut v := 2 +fn foo(mut v []int) { v[0] = 1 } +mut v := [20] foo(mut v) -assert v < 4 +assert v[0] < 4 ``` An `assert` statement checks that its expression evaluates to `true`. If an assert fails, the program will abort. Asserts should only be used to detect programming errors. When an @@ -2101,7 +2189,7 @@ fn main() { println(hello()) } ``` -```v +```v failcompile module main // hello_test.v fn test_hello() { @@ -2150,16 +2238,19 @@ during compilation. If your V program compiles, it's guaranteed that it's going to be leak free. For example: ```v -fn draw_text(s string, x, y int) { - ... +import strings +fn draw_text(s string, x int, y int) { + // ... } fn draw_scene() { - ... + // ... + name1 := 'abc' + name2 := 'def ghi' draw_text('hello $name1', 10, 10) draw_text('hello $name2', 100, 10) - draw_text(strings.repeat('X', 10000), 10, 50) - ... + draw_text(strings.repeat(`X`, 10000), 10, 50) + // ... } ``` @@ -2171,6 +2262,7 @@ These two strings are small, V will use a preallocated buffer for them. ```v +struct User{ name string } fn test() []int { number := 7 // stack variable user := User{} // struct allocated on stack @@ -2200,6 +2292,7 @@ V's ORM provides a number of benefits: then manually construct objects from the parsed results.) ```v +import sqlite struct Customer { // struct name has to be the same as the table name (for now) id int // a field named `id` of integer type must be the first field name string @@ -2207,7 +2300,7 @@ struct Customer { // struct name has to be the same as the table name (for now) country string } -db := sqlite.connect('customers.db') +db := sqlite.connect('customers.db')? // select count(*) from Customer nr_customers := sql db { select count from Customer } @@ -2260,7 +2353,7 @@ To generate documentation use vdoc, for example `v doc net.http`. You don't need to worry about formatting your code or setting style guidelines. `v fmt` takes care of that: -```v +```shell v fmt file.v ``` @@ -2314,9 +2407,9 @@ Examples of potentially memory-unsafe operations are: To mark potentially memory-unsafe operations, enclose them in an `unsafe` block: -```v +```v failcompile // allocate 2 uninitialized bytes & return a reference to them -mut p := unsafe { &byte(malloc(2)) } +mut p := unsafe { malloc(2) } p[0] = `h` // Error: pointer indexing is only allowed in `unsafe` blocks unsafe { p[0] = `h` // OK @@ -2409,7 +2502,7 @@ Currently the `linux`, `darwin` , `freebsd`, and `windows` flags are supported. NB: Each flag must go on its own line (for now) -```v +```v oksyntax #flag linux -lsdl2 #flag linux -Ivig #flag linux -DCIMGUI_DEFINE_ENUMS_AND_STRUCTS=1 @@ -2428,7 +2521,7 @@ freedesktop one. If no flags are passed it will add `--cflags` and `--libs`, both lines below do the same: -```v +```v oksyntax #pkgconfig r_core #pkgconfig --cflags --libs r_core ``` @@ -2445,7 +2538,7 @@ Then: * Put a v.mod file inside the toplevel folder of your module (if you created your module with `v new` you already have v.mod file). For example: -```v +```v ignore Module { name: 'mymodule', description: 'My nice module wraps a simple C library.', @@ -2456,7 +2549,7 @@ Module { * Add these lines to the top of your module: -```v +```v oksyntax #flag -I @VROOT/c #flag @VROOT/c/implementation.o #include "header.h" @@ -2605,7 +2698,7 @@ eprintln( 'file: ' + @FILE + ' | line: ' + @LINE + ' | fn: ' + @MOD + '.' + @FN) ``` Another example, is if you want to embed the version/name from v.mod *inside* your executable: -```v +```v ignore import v.vmod vm := vmod.decode( @VMOD_FILE ) or { panic(err) } eprintln('$vm.name $vm.version\n $vm.description') @@ -2648,7 +2741,7 @@ the boolean expression is highly improbable. In the JS backend, that does nothin Having built-in JSON support is nice, but V also allows you to create efficient serializers for any data format. V has compile-time `if` and `for` constructs: -```v +```v wip // TODO: not fully implemented struct User { @@ -2732,7 +2825,7 @@ To improve safety and maintainability, operator overloading is limited: TODO: not implemented yet -```v +```v failcompile fn main() { a := 10 asm x64 { @@ -2767,7 +2860,7 @@ int main() { Run `v translate test.cpp` and V will generate `test.v`: ```v -fn main { +fn main() { mut s := []string{} s << 'V is ' s << 'awesome' @@ -2825,13 +2918,13 @@ More examples, including a graphical application: To cross compile your project simply run -```v +```shell v -os windows . ``` or -```v +```shell v -os linux . ``` @@ -2853,7 +2946,7 @@ cross-platform support. "V scripts" run on Unix-like systems as well as on Windo Use the `.vsh` file extension. It will make all functions in the `os` module global (so that you can use `ls()` instead of `os.ls()`, for example). -```v +```v wip #!/usr/local/bin/v run // The shebang above associates the file to V on Unix-like systems, // so it can be run just by specifying the path to the file @@ -2930,7 +3023,7 @@ fn C.DefWindowProc(hwnd int, msg int, lparam int, wparam int) V has 41 reserved keywords (3 are literals): -```v +```v ignore as asm assert @@ -2978,7 +3071,7 @@ See also [Types](#types). This lists operators for [primitive types](#primitive-types) only. -```v +```v ignore + sum integers, floats, strings - difference integers, floats * product integers, floats diff --git a/doc/upcoming.md b/doc/upcoming.md index 24f0c57fcd..c6970b052f 100644 --- a/doc/upcoming.md +++ b/doc/upcoming.md @@ -22,7 +22,7 @@ Objects that are supposed to be used to exchange data between coroutines have to be declared with special care. Exactly one of the following 4 kinds of declaration has to be chosen: -```v +```v ignore a := ... mut b := ... shared c := ... @@ -41,14 +41,14 @@ atomic d := ... *concurrently*.2 In order to avoid data races it has to be locked before access can occur and unlocked to allow access to other coroutines. This is done by one the following block structures: - ```v + ```v ignore lock c { // read, modify, write c ... } ``` - ```v + ```v ignore rlock c { // read c ... @@ -122,7 +122,7 @@ Outside of `lock`/`rlock` blocks function arguments must in general match - with the familiar exception that objects declared `mut` can be used to call functions expecting immutable arguments: -```v +```v ignore fn f(x St) {...} fn g(mut x St) {...} fn h(shared x St) {...} @@ -145,7 +145,7 @@ i(atomic d) Inside a `lock c {...}` block `c` behaves like a `mut`, inside an `rlock c {...}` block like an immutable: -```v +```v ignore shared c := &St{...} lock c { g(mut c) @@ -165,7 +165,7 @@ object is accessed outside of any corresponding `lock`/`rlock` block. However in simple and obvious cases the necessary lock/unlock can be generated automatically for `array`/`map` operations: -```v +```v ignore shared a []int{...} go h2(shared a) a << 3