ci/tools: check-md.v: extract examples and check they are compilable (#6719)

pull/6729/head
Delyan Angelov 2020-11-03 02:04:14 +02:00 committed by GitHub
parent ae241785bf
commit f32c6784e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 376 additions and 97 deletions

View File

@ -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

View File

@ -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
}

View File

@ -96,10 +96,20 @@ Anything you can do in other languages, you can do in V.
</td></tr>
</table>
<!--
There are several special keywords, which you can put after the code fences for v.
These are:
compile - default, you do not need to specify it. cmd/tools/check-md.v compile the example.
ignore - ignore the example, useful for examples that just use the syntax highlighting
failcompile - known failing compilation. Useful for examples demonstrating compiler errors.
oksyntax - it should parse, it may not compile. Useful for partial examples.
badsyntax - known bad syntax, it should not even parse
wip - like ignore; a planned feature; easy to search.
-->
## 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,6 +1140,12 @@ struct ButtonConfig {
height int = 20
}
struct Button {
text string
width int
height int
}
fn new_button(c ButtonConfig) &Button {
return &Button{
width: c.width
@ -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<T> {
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<T> {
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,7 +2010,15 @@ 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
```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`
@ -1958,6 +2033,7 @@ select {
// 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

View File

@ -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*.<sup>2</sup> 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