ci/tools: check-md.v: extract examples and check they are compilable (#6719)
							parent
							
								
									ae241785bf
								
							
						
					
					
						commit
						f32c6784e7
					
				| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										259
									
								
								doc/docs.md
								
								
								
								
							
							
						
						
									
										259
									
								
								doc/docs.md
								
								
								
								
							| 
						 | 
				
			
			@ -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,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<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,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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue