mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
ci/tools: check-md.v: extract examples and check they are compilable (#6719)
This commit is contained in:
parent
ae241785bf
commit
f32c6784e7
2
.github/workflows/docs_ci.yml
vendored
2
.github/workflows/docs_ci.yml
vendored
@ -19,5 +19,5 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Build V
|
- name: Build V
|
||||||
run: make
|
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
|
run: ./v run cmd/tools/check-md.v doc/docs.md doc/upcoming.md CHANGELOG.md
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
module main
|
module main
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import rand
|
||||||
|
import term
|
||||||
|
import v.pref
|
||||||
|
|
||||||
const (
|
const (
|
||||||
too_long_line_length = 100
|
too_long_line_length = 100
|
||||||
|
term_colors = term.can_show_color_on_stderr()
|
||||||
)
|
)
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
files_paths := os.args[1..]
|
files_paths := os.args[1..]
|
||||||
mut warnings := 0
|
mut warnings := 0
|
||||||
mut errors := 0
|
mut errors := 0
|
||||||
|
mut oks := 0
|
||||||
|
mut all_md_files := []MDFile{}
|
||||||
for file_path in files_paths {
|
for file_path in files_paths {
|
||||||
real_path := os.real_path(file_path)
|
real_path := os.real_path(file_path)
|
||||||
lines := os.read_lines(real_path) or {
|
lines := os.read_lines(real_path) or {
|
||||||
@ -17,27 +23,207 @@ fn main() {
|
|||||||
warnings++
|
warnings++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
mut mdfile := MDFile{
|
||||||
|
path: file_path
|
||||||
|
}
|
||||||
for i, line in lines {
|
for i, line in lines {
|
||||||
if line.len > too_long_line_length {
|
if line.len > too_long_line_length {
|
||||||
linetrace_msg := '$file_path:${i + 1}:${line.len + 1}: '
|
|
||||||
if line.starts_with('|') {
|
if line.starts_with('|') {
|
||||||
println(linetrace_msg + 'long table (warn)')
|
println(wline(file_path, i, line.len, 'long table'))
|
||||||
warnings++
|
warnings++
|
||||||
} else if line.contains('https') {
|
} else if line.contains('https') {
|
||||||
println(linetrace_msg + 'long link (warn)')
|
println(wline(file_path, i, line.len, 'long link'))
|
||||||
warnings++
|
warnings++
|
||||||
} else {
|
} else {
|
||||||
eprintln(linetrace_msg + 'line too long')
|
eprintln(eline(file_path, i, line.len, 'line too long'))
|
||||||
errors++
|
errors++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mdfile.parse_line(i, line)
|
||||||
}
|
}
|
||||||
|
all_md_files << mdfile
|
||||||
}
|
}
|
||||||
if warnings > 0 || errors > 0 {
|
for mut mdfile in all_md_files {
|
||||||
println('\nWarnings | Errors')
|
new_errors, new_oks := mdfile.check_examples()
|
||||||
println('$warnings\t | $errors')
|
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 {
|
if errors > 0 {
|
||||||
exit(1)
|
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
|
||||||
|
}
|
||||||
|
253
doc/docs.md
253
doc/docs.md
@ -96,10 +96,20 @@ Anything you can do in other languages, you can do in V.
|
|||||||
</td></tr>
|
</td></tr>
|
||||||
</table>
|
</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
|
## Hello World
|
||||||
|
|
||||||
|
|
||||||
```v
|
```v
|
||||||
fn main() {
|
fn main() {
|
||||||
println('hello world')
|
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 `=`.
|
Note the (important) difference between `:=` and `=`.
|
||||||
`:=` is used for declaring and initializing, `=` is used for assigning.
|
`:=` is used for declaring and initializing, `=` is used for assigning.
|
||||||
|
|
||||||
```v
|
```v failcompile
|
||||||
fn main() {
|
fn main() {
|
||||||
age = 21
|
age = 21
|
||||||
}
|
}
|
||||||
@ -304,7 +314,7 @@ that is already used in a parent scope will cause a compilation error.
|
|||||||
|
|
||||||
### Primitive types
|
### Primitive types
|
||||||
|
|
||||||
```v
|
```v ignore
|
||||||
bool
|
bool
|
||||||
|
|
||||||
string
|
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.
|
completely into the data range of the type on the other side.
|
||||||
These are the allowed possibilities:
|
These are the allowed possibilities:
|
||||||
|
|
||||||
```
|
```v ignore
|
||||||
i8 → i16 → int → i64
|
i8 → i16 → int → i64
|
||||||
↘ ↘
|
↘ ↘
|
||||||
f32 → f64
|
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.
|
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:
|
String values are immutable. You cannot mutate elements:
|
||||||
|
|
||||||
```v
|
```v failcompile
|
||||||
mut s := 'hello 🌎'
|
mut s := 'hello 🌎'
|
||||||
s[0] = `H` // not allowed
|
s[0] = `H` // not allowed
|
||||||
```
|
```
|
||||||
@ -411,6 +421,7 @@ println('[${int(x):-10}]') // pad with spaces on the right
|
|||||||
### String operators
|
### String operators
|
||||||
|
|
||||||
```v
|
```v
|
||||||
|
name := 'Bob'
|
||||||
bobby := name + 'by' // + is used to concatenate strings
|
bobby := name + 'by' // + is used to concatenate strings
|
||||||
println(bobby) // "Bobby"
|
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.
|
All operators in V must have values of the same type on both sides.
|
||||||
You cannot concatenate an integer to a string:
|
You cannot concatenate an integer to a string:
|
||||||
|
|
||||||
```v
|
```v failcompile
|
||||||
age := 10
|
age := 10
|
||||||
println('age = ' + age) // not allowed
|
println('age = ' + age) // not allowed
|
||||||
```
|
```
|
||||||
@ -430,12 +441,14 @@ println('age = ' + age) // not allowed
|
|||||||
We have to either convert `age` to a `string`:
|
We have to either convert `age` to a `string`:
|
||||||
|
|
||||||
```v
|
```v
|
||||||
|
age := 11
|
||||||
println('age = ' + age.str())
|
println('age = ' + age.str())
|
||||||
```
|
```
|
||||||
|
|
||||||
or use string interpolation (preferred):
|
or use string interpolation (preferred):
|
||||||
|
|
||||||
```v
|
```v
|
||||||
|
age := 12
|
||||||
println('age = $age')
|
println('age = $age')
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -688,7 +701,7 @@ println('Your OS is ${os}.')
|
|||||||
Any imported module name can be aliased using the `as` keyword:
|
Any imported module name can be aliased using the `as` keyword:
|
||||||
|
|
||||||
NOTE: this example will not compile unless you have created `mymod/sha256.v`
|
NOTE: this example will not compile unless you have created `mymod/sha256.v`
|
||||||
```v
|
```v failcompile
|
||||||
import crypto.sha256
|
import crypto.sha256
|
||||||
import mymod.sha256 as mysha256
|
import mymod.sha256 as mysha256
|
||||||
|
|
||||||
@ -712,7 +725,7 @@ fn (mut t MyTime) century() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
my_time := MyTime{
|
mut my_time := MyTime{
|
||||||
year: 2020,
|
year: 2020,
|
||||||
month: 12,
|
month: 12,
|
||||||
day: 25
|
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.
|
If you have a struct field which should be checked, there is also a way to name an alias.
|
||||||
```v
|
```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 {
|
if x.bar is MyStruct as bar {
|
||||||
// x.bar cannot be cast automatically
|
// x.bar cannot be cast automatically
|
||||||
// you must explicitly state "as bar" to create a variable with the MyStruct type
|
// 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:
|
It's also useful for writing boolean expressions that are clearer and more compact:
|
||||||
|
|
||||||
```v
|
```v
|
||||||
|
enum Token { plus minus div mult }
|
||||||
|
struct Parser { token Token }
|
||||||
|
parser := Parser{}
|
||||||
if parser.token == .plus || parser.token == .minus ||
|
if parser.token == .plus || parser.token == .minus ||
|
||||||
parser.token == .div || parser.token == .mult {
|
parser.token == .div || parser.token == .mult {
|
||||||
...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
if parser.token in [.plus, .minus, .div, .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.
|
until the surrounding function returns.
|
||||||
|
|
||||||
```v
|
```v
|
||||||
|
import os
|
||||||
|
|
||||||
fn read_log() {
|
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() }
|
defer { f.close() }
|
||||||
...
|
// ...
|
||||||
if !ok {
|
if !ok {
|
||||||
// defer statement will be called here, the file will be closed
|
// defer statement will be called here, the file will be closed
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
...
|
// ...
|
||||||
// defer statement will be called here, the file will be closed
|
// 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:
|
and get a reference to it, use the `&` prefix:
|
||||||
|
|
||||||
```v
|
```v
|
||||||
|
struct Point {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
p := &Point{10, 10}
|
p := &Point{10, 10}
|
||||||
// References have the same syntax for accessing fields
|
// References have the same syntax for accessing fields
|
||||||
println(p.x)
|
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 doesn't allow subclassing, but it supports embedded structs:
|
||||||
|
|
||||||
```v
|
```v wip
|
||||||
// TODO: this will be implemented later
|
// TODO: this will be implemented later
|
||||||
struct Button {
|
struct Button {
|
||||||
Widget
|
Widget
|
||||||
@ -1085,6 +1113,10 @@ It's also possible to define custom default values.
|
|||||||
### Short struct literal syntax
|
### Short struct literal syntax
|
||||||
|
|
||||||
```v
|
```v
|
||||||
|
struct Point{
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
mut p := Point{x: 10, y: 20}
|
mut p := Point{x: 10, y: 20}
|
||||||
|
|
||||||
// you can omit the struct name when it's already known
|
// you can omit the struct name when it's already known
|
||||||
@ -1108,6 +1140,12 @@ struct ButtonConfig {
|
|||||||
height int = 20
|
height int = 20
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Button {
|
||||||
|
text string
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
}
|
||||||
|
|
||||||
fn new_button(c ButtonConfig) &Button {
|
fn new_button(c ButtonConfig) &Button {
|
||||||
return &Button{
|
return &Button{
|
||||||
width: c.width
|
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:
|
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})
|
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:
|
For example, here's the `string` type defined in the `builtin` module:
|
||||||
|
|
||||||
```v
|
```v ignore
|
||||||
struct string {
|
struct string {
|
||||||
str byteptr
|
str byteptr
|
||||||
pub:
|
pub:
|
||||||
@ -1164,7 +1202,7 @@ pub:
|
|||||||
It's easy to see from this definition that `string` is an immutable type.
|
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 byte pointer with the string data is not accessible outside `builtin` at all.
|
||||||
The `len` field is public, but immutable:
|
The `len` field is public, but immutable:
|
||||||
```v
|
```v failcompile
|
||||||
fn main() {
|
fn main() {
|
||||||
str := 'hello'
|
str := 'hello'
|
||||||
len := str.len // OK
|
len := str.len // OK
|
||||||
@ -1222,6 +1260,7 @@ It is possible to modify function arguments by using the keyword `mut`:
|
|||||||
|
|
||||||
```v
|
```v
|
||||||
struct User {
|
struct User {
|
||||||
|
name string
|
||||||
mut:
|
mut:
|
||||||
is_registered bool
|
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 makes it easy to return a modified version of an object:
|
||||||
|
|
||||||
```v
|
```v
|
||||||
|
struct User{ name string age int is_registered bool }
|
||||||
fn register(u User) User {
|
fn register(u User) User {
|
||||||
return { u | is_registered: true }
|
return { u | is_registered: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mut user := User{name: 'abc' age: 23}
|
||||||
user = register(user)
|
user = register(user)
|
||||||
|
println(user)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Anonymous & high order functions
|
### Anonymous & high order functions
|
||||||
@ -1304,12 +1346,13 @@ fn main() {
|
|||||||
## References
|
## References
|
||||||
|
|
||||||
```v
|
```v
|
||||||
|
struct Foo{}
|
||||||
fn (foo Foo) bar_method() {
|
fn (foo Foo) bar_method() {
|
||||||
...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bar_function(foo Foo) {
|
fn bar_function(foo Foo) {
|
||||||
...
|
// ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1324,6 +1367,8 @@ You can ensure that the struct is always passed by reference by
|
|||||||
adding `&`:
|
adding `&`:
|
||||||
|
|
||||||
```v
|
```v
|
||||||
|
struct Foo{ abc int }
|
||||||
|
|
||||||
fn (foo &Foo) bar() {
|
fn (foo &Foo) bar() {
|
||||||
println(foo.abc)
|
println(foo.abc)
|
||||||
}
|
}
|
||||||
@ -1333,9 +1378,9 @@ fn (foo &Foo) bar() {
|
|||||||
`(mut foo Foo)` must be used.
|
`(mut foo Foo)` must be used.
|
||||||
|
|
||||||
In general, V's references are similar to Go pointers and C++ references.
|
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> {
|
struct Node<T> {
|
||||||
val T
|
val T
|
||||||
left &Node
|
left &Node
|
||||||
@ -1394,9 +1439,7 @@ They can represent complex structures, and this is used quite often since there
|
|||||||
are no globals:
|
are no globals:
|
||||||
-->
|
-->
|
||||||
|
|
||||||
```v
|
```v ignore
|
||||||
println('Top cities: $TOP_CITIES.filter(.usa)')
|
|
||||||
vs
|
|
||||||
println('Top cities: $top_cities.filter(.usa)')
|
println('Top cities: $top_cities.filter(.usa)')
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1406,6 +1449,7 @@ println('Top cities: $top_cities.filter(.usa)')
|
|||||||
strings, numbers, arrays, maps, structs.
|
strings, numbers, arrays, maps, structs.
|
||||||
|
|
||||||
```v
|
```v
|
||||||
|
struct User{ name string age int }
|
||||||
println(1) // "1"
|
println(1) // "1"
|
||||||
println('hi') // "hi"
|
println('hi') // "hi"
|
||||||
println([1,2,3]) // "[1, 2, 3]"
|
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:
|
The number of builtin functions is low. Other builtin functions are:
|
||||||
|
|
||||||
|
|
||||||
```
|
```v ignore
|
||||||
fn exit(exit_code int)
|
fn exit(exit_code int)
|
||||||
fn panic(message string)
|
fn panic(message string)
|
||||||
fn print_backtrace()
|
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
|
To create a new module, create a directory with your module's name containing
|
||||||
.v files with code:
|
.v files with code:
|
||||||
|
|
||||||
```
|
```shell
|
||||||
cd ~/code/modules
|
cd ~/code/modules
|
||||||
mkdir mymodule
|
mkdir mymodule
|
||||||
vim mymodule/myfile.v
|
vim mymodule/myfile.v
|
||||||
```
|
```
|
||||||
```v
|
```v failcompile
|
||||||
// myfile.v
|
// myfile.v
|
||||||
module mymodule
|
module mymodule
|
||||||
|
|
||||||
@ -1466,7 +1510,7 @@ pub fn say_hi() {
|
|||||||
|
|
||||||
You can now use `mymodule` in your code:
|
You can now use `mymodule` in your code:
|
||||||
|
|
||||||
```v
|
```v failcompile
|
||||||
import mymodule
|
import mymodule
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -1574,14 +1618,19 @@ struct Venus {}
|
|||||||
type World = Moon | Mars | Venus
|
type World = Moon | Mars | Venus
|
||||||
|
|
||||||
sum := World(Moon{})
|
sum := World(Moon{})
|
||||||
|
println(sum)
|
||||||
```
|
```
|
||||||
|
|
||||||
To check whether a sum type instance holds a certain type, use `sum is Type`.
|
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`:
|
To cast a sum type to one of its variants you can use `sum as Type`:
|
||||||
|
|
||||||
```v
|
```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() {
|
fn main() {
|
||||||
mut w := World(Moon{})
|
mut w := World(Moon{})
|
||||||
assert w is Moon
|
assert w is Moon
|
||||||
@ -1600,8 +1649,11 @@ fn main() {
|
|||||||
You can also use `match` to determine the variant:
|
You can also use `match` to determine the variant:
|
||||||
|
|
||||||
```v
|
```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) {
|
fn land(w World) {
|
||||||
match w {
|
match w {
|
||||||
Moon {} // no atmosphere
|
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
|
- using `as` to specify a variable name
|
||||||
|
|
||||||
```v
|
```v
|
||||||
fn (m Moon) moon_walk()
|
struct Moon {}
|
||||||
fn (m Mars) shiver()
|
struct Mars {}
|
||||||
fn (v Venus) sweat()
|
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) {
|
fn pass_time(w World) {
|
||||||
match w {
|
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
|
`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.
|
to the `error()` function. `err` is empty if `none` was returned.
|
||||||
|
|
||||||
```v
|
```v oksyntax
|
||||||
user := repo.find_user_by_id(7) or {
|
user := repo.find_user_by_id(7) or {
|
||||||
println(err) // "User 7 not found"
|
println(err) // "User 7 not found"
|
||||||
return
|
return
|
||||||
@ -1730,7 +1788,7 @@ any further.
|
|||||||
|
|
||||||
The body of `f` is essentially a condensed version of:
|
The body of `f` is essentially a condensed version of:
|
||||||
|
|
||||||
```v
|
```v ignore
|
||||||
resp := http.get(url) or {
|
resp := http.get(url) or {
|
||||||
return error(err)
|
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:
|
The second method is to break from execution early:
|
||||||
|
|
||||||
```v
|
```v oksyntax
|
||||||
user := repo.find_user_by_id(7) or {
|
user := repo.find_user_by_id(7) or {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1767,13 +1825,16 @@ fn do_something(s string) ?string {
|
|||||||
|
|
||||||
a := do_something('foo') or { 'default' } // a will be 'foo'
|
a := do_something('foo') or { 'default' } // a will be 'foo'
|
||||||
b := do_something('bar') or { 'default' } // b will be 'default'
|
b := do_something('bar') or { 'default' } // b will be 'default'
|
||||||
|
println(a)
|
||||||
|
println(b)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
The fourth method is to use `if` unwrapping:
|
The fourth method is to use `if` unwrapping:
|
||||||
|
|
||||||
```v
|
```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
|
println(resp.text) // resp is a http.Response, not an optional
|
||||||
} else {
|
} else {
|
||||||
println(err)
|
println(err)
|
||||||
@ -1784,7 +1845,8 @@ Above, `http.get` returns a `?http.Response`. `resp` is only in scope for the fi
|
|||||||
|
|
||||||
## Generics
|
## Generics
|
||||||
|
|
||||||
```v
|
```v wip
|
||||||
|
|
||||||
struct Repo<T> {
|
struct Repo<T> {
|
||||||
db DB
|
db DB
|
||||||
}
|
}
|
||||||
@ -1897,13 +1959,13 @@ variables:
|
|||||||
|
|
||||||
```v
|
```v
|
||||||
fn f(ch chan int) {
|
fn f(ch chan int) {
|
||||||
...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
...
|
ch := chan int{}
|
||||||
go f(ch)
|
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:
|
pop objects from the other end:
|
||||||
|
|
||||||
```v
|
```v
|
||||||
|
mut ch := chan int{}
|
||||||
|
mut ch2 := chan f64{}
|
||||||
n := 5
|
n := 5
|
||||||
x := 7.3
|
x := 7.3
|
||||||
ch <- n // push
|
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
|
associated channel has been closed and the buffer is empty. This situation can be
|
||||||
handled using an or branch (see [Handling Optionals](#handling-optionals)).
|
handled using an or branch (see [Handling Optionals](#handling-optionals)).
|
||||||
|
|
||||||
```v
|
```v wip
|
||||||
|
mut ch := chan int{}
|
||||||
|
mut ch2 := chan f64{}
|
||||||
|
// ...
|
||||||
ch.close()
|
ch.close()
|
||||||
...
|
// ...
|
||||||
m := <-ch or {
|
m := <-ch or {
|
||||||
println('channel has been closed')
|
println('channel has been closed')
|
||||||
}
|
}
|
||||||
@ -1943,8 +2010,16 @@ y := <-ch2 ?
|
|||||||
The `select` command allows monitoring several channels at the same time
|
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
|
without noticeable CPU load. It consists of a list of possible transfers and associated branches
|
||||||
of statements - similar to the [match](#match) command:
|
of statements - similar to the [match](#match) command:
|
||||||
```v
|
```v wip
|
||||||
select {
|
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 {
|
a := <-ch {
|
||||||
// do something with `a`
|
// do something with `a`
|
||||||
}
|
}
|
||||||
@ -1957,6 +2032,7 @@ select {
|
|||||||
> 500 * time.millisecond {
|
> 500 * time.millisecond {
|
||||||
// do something if no channel has become ready within 0.5s
|
// do something if no channel has become ready within 0.5s
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -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`
|
The `select` command can be used as an *expression* of type `bool`
|
||||||
that becomes `false` if all channels are closed:
|
that becomes `false` if all channels are closed:
|
||||||
```v
|
```v wip
|
||||||
if select {
|
if select {
|
||||||
ch <- a {
|
ch <- a {
|
||||||
...
|
// ...
|
||||||
}
|
}
|
||||||
} {
|
} {
|
||||||
// channel was open
|
// channel was open
|
||||||
@ -1982,10 +2058,21 @@ if select {
|
|||||||
|
|
||||||
For special purposes there are some builtin properties and methods:
|
For special purposes there are some builtin properties and methods:
|
||||||
```v
|
```v
|
||||||
|
struct Abc{x int}
|
||||||
|
|
||||||
|
a := 2.13
|
||||||
|
mut ch := chan f64{}
|
||||||
|
|
||||||
res := ch.try_push(a) // try to perform `ch <- a`
|
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
|
l := ch.len // number of elements in queue
|
||||||
c := ch.cap // maximum queue length
|
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
|
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
|
`.success`, `.not_ready` or `.closed` - dependent on whether the object has been transferred or
|
||||||
@ -2007,12 +2094,12 @@ mut:
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (mut b St) g() {
|
fn (mut b St) g() {
|
||||||
...
|
|
||||||
b.mtx.m_lock()
|
b.mtx.m_lock()
|
||||||
// read/modify/write b.x
|
// read/modify/write b.x
|
||||||
...
|
|
||||||
b.mtx.unlock()
|
b.mtx.unlock()
|
||||||
...
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn caller() {
|
fn caller() {
|
||||||
@ -2021,12 +2108,12 @@ fn caller() {
|
|||||||
mtx: sync.new_mutex()
|
mtx: sync.new_mutex()
|
||||||
}
|
}
|
||||||
go a.g()
|
go a.g()
|
||||||
...
|
|
||||||
a.mtx.m_lock()
|
a.mtx.m_lock()
|
||||||
// read/modify/write a.x
|
// read/modify/write a.x
|
||||||
...
|
|
||||||
a.mtx.unlock()
|
a.mtx.unlock()
|
||||||
...
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -2079,9 +2166,10 @@ No runtime reflection is used. This results in much better performance.
|
|||||||
### Asserts
|
### Asserts
|
||||||
|
|
||||||
```v
|
```v
|
||||||
mut v := 2
|
fn foo(mut v []int) { v[0] = 1 }
|
||||||
|
mut v := [20]
|
||||||
foo(mut v)
|
foo(mut v)
|
||||||
assert v < 4
|
assert v[0] < 4
|
||||||
```
|
```
|
||||||
An `assert` statement checks that its expression evaluates to `true`. If an assert fails,
|
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
|
the program will abort. Asserts should only be used to detect programming errors. When an
|
||||||
@ -2101,7 +2189,7 @@ fn main() {
|
|||||||
println(hello())
|
println(hello())
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
```v
|
```v failcompile
|
||||||
module main
|
module main
|
||||||
// hello_test.v
|
// hello_test.v
|
||||||
fn test_hello() {
|
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:
|
to be leak free. For example:
|
||||||
|
|
||||||
```v
|
```v
|
||||||
fn draw_text(s string, x, y int) {
|
import strings
|
||||||
...
|
fn draw_text(s string, x int, y int) {
|
||||||
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_scene() {
|
fn draw_scene() {
|
||||||
...
|
// ...
|
||||||
|
name1 := 'abc'
|
||||||
|
name2 := 'def ghi'
|
||||||
draw_text('hello $name1', 10, 10)
|
draw_text('hello $name1', 10, 10)
|
||||||
draw_text('hello $name2', 100, 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 will use a preallocated buffer for them.
|
||||||
|
|
||||||
```v
|
```v
|
||||||
|
struct User{ name string }
|
||||||
fn test() []int {
|
fn test() []int {
|
||||||
number := 7 // stack variable
|
number := 7 // stack variable
|
||||||
user := User{} // struct allocated on stack
|
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.)
|
then manually construct objects from the parsed results.)
|
||||||
|
|
||||||
```v
|
```v
|
||||||
|
import sqlite
|
||||||
struct Customer { // struct name has to be the same as the table name (for now)
|
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
|
id int // a field named `id` of integer type must be the first field
|
||||||
name string
|
name string
|
||||||
@ -2207,7 +2300,7 @@ struct Customer { // struct name has to be the same as the table name (for now)
|
|||||||
country string
|
country string
|
||||||
}
|
}
|
||||||
|
|
||||||
db := sqlite.connect('customers.db')
|
db := sqlite.connect('customers.db')?
|
||||||
|
|
||||||
// select count(*) from Customer
|
// select count(*) from Customer
|
||||||
nr_customers := sql 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.
|
You don't need to worry about formatting your code or setting style guidelines.
|
||||||
`v fmt` takes care of that:
|
`v fmt` takes care of that:
|
||||||
|
|
||||||
```v
|
```shell
|
||||||
v fmt file.v
|
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:
|
To mark potentially memory-unsafe operations, enclose them in an `unsafe` block:
|
||||||
|
|
||||||
```v
|
```v failcompile
|
||||||
// allocate 2 uninitialized bytes & return a reference to them
|
// 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
|
p[0] = `h` // Error: pointer indexing is only allowed in `unsafe` blocks
|
||||||
unsafe {
|
unsafe {
|
||||||
p[0] = `h` // OK
|
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)
|
NB: Each flag must go on its own line (for now)
|
||||||
|
|
||||||
```v
|
```v oksyntax
|
||||||
#flag linux -lsdl2
|
#flag linux -lsdl2
|
||||||
#flag linux -Ivig
|
#flag linux -Ivig
|
||||||
#flag linux -DCIMGUI_DEFINE_ENUMS_AND_STRUCTS=1
|
#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:
|
If no flags are passed it will add `--cflags` and `--libs`, both lines below do the same:
|
||||||
|
|
||||||
```v
|
```v oksyntax
|
||||||
#pkgconfig r_core
|
#pkgconfig r_core
|
||||||
#pkgconfig --cflags --libs 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
|
* 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
|
created your module with `v new` you already have v.mod file). For
|
||||||
example:
|
example:
|
||||||
```v
|
```v ignore
|
||||||
Module {
|
Module {
|
||||||
name: 'mymodule',
|
name: 'mymodule',
|
||||||
description: 'My nice module wraps a simple C library.',
|
description: 'My nice module wraps a simple C library.',
|
||||||
@ -2456,7 +2549,7 @@ Module {
|
|||||||
|
|
||||||
|
|
||||||
* Add these lines to the top of your module:
|
* Add these lines to the top of your module:
|
||||||
```v
|
```v oksyntax
|
||||||
#flag -I @VROOT/c
|
#flag -I @VROOT/c
|
||||||
#flag @VROOT/c/implementation.o
|
#flag @VROOT/c/implementation.o
|
||||||
#include "header.h"
|
#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:
|
Another example, is if you want to embed the version/name from v.mod *inside* your executable:
|
||||||
```v
|
```v ignore
|
||||||
import v.vmod
|
import v.vmod
|
||||||
vm := vmod.decode( @VMOD_FILE ) or { panic(err) }
|
vm := vmod.decode( @VMOD_FILE ) or { panic(err) }
|
||||||
eprintln('$vm.name $vm.version\n $vm.description')
|
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
|
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:
|
serializers for any data format. V has compile-time `if` and `for` constructs:
|
||||||
|
|
||||||
```v
|
```v wip
|
||||||
// TODO: not fully implemented
|
// TODO: not fully implemented
|
||||||
|
|
||||||
struct User {
|
struct User {
|
||||||
@ -2732,7 +2825,7 @@ To improve safety and maintainability, operator overloading is limited:
|
|||||||
|
|
||||||
TODO: not implemented yet
|
TODO: not implemented yet
|
||||||
|
|
||||||
```v
|
```v failcompile
|
||||||
fn main() {
|
fn main() {
|
||||||
a := 10
|
a := 10
|
||||||
asm x64 {
|
asm x64 {
|
||||||
@ -2767,7 +2860,7 @@ int main() {
|
|||||||
Run `v translate test.cpp` and V will generate `test.v`:
|
Run `v translate test.cpp` and V will generate `test.v`:
|
||||||
|
|
||||||
```v
|
```v
|
||||||
fn main {
|
fn main() {
|
||||||
mut s := []string{}
|
mut s := []string{}
|
||||||
s << 'V is '
|
s << 'V is '
|
||||||
s << 'awesome'
|
s << 'awesome'
|
||||||
@ -2825,13 +2918,13 @@ More examples, including a graphical application:
|
|||||||
|
|
||||||
To cross compile your project simply run
|
To cross compile your project simply run
|
||||||
|
|
||||||
```v
|
```shell
|
||||||
v -os windows .
|
v -os windows .
|
||||||
```
|
```
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
```v
|
```shell
|
||||||
v -os linux .
|
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`
|
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).
|
module global (so that you can use `ls()` instead of `os.ls()`, for example).
|
||||||
|
|
||||||
```v
|
```v wip
|
||||||
#!/usr/local/bin/v run
|
#!/usr/local/bin/v run
|
||||||
// The shebang above associates the file to V on Unix-like systems,
|
// 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
|
// 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 has 41 reserved keywords (3 are literals):
|
||||||
|
|
||||||
```v
|
```v ignore
|
||||||
as
|
as
|
||||||
asm
|
asm
|
||||||
assert
|
assert
|
||||||
@ -2978,7 +3071,7 @@ See also [Types](#types).
|
|||||||
|
|
||||||
This lists operators for [primitive types](#primitive-types) only.
|
This lists operators for [primitive types](#primitive-types) only.
|
||||||
|
|
||||||
```v
|
```v ignore
|
||||||
+ sum integers, floats, strings
|
+ sum integers, floats, strings
|
||||||
- difference integers, floats
|
- difference integers, floats
|
||||||
* product 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
|
coroutines have to be declared with special care. Exactly one of the following
|
||||||
4 kinds of declaration has to be chosen:
|
4 kinds of declaration has to be chosen:
|
||||||
|
|
||||||
```v
|
```v ignore
|
||||||
a := ...
|
a := ...
|
||||||
mut b := ...
|
mut b := ...
|
||||||
shared c := ...
|
shared c := ...
|
||||||
@ -41,14 +41,14 @@ atomic d := ...
|
|||||||
*concurrently*.<sup>2</sup> In order to avoid data races it has to
|
*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
|
be locked before access can occur and unlocked to allow access to
|
||||||
other coroutines. This is done by one the following block structures:
|
other coroutines. This is done by one the following block structures:
|
||||||
```v
|
```v ignore
|
||||||
lock c {
|
lock c {
|
||||||
// read, modify, write c
|
// read, modify, write c
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```v
|
```v ignore
|
||||||
rlock c {
|
rlock c {
|
||||||
// read 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
|
match - with the familiar exception that objects declared `mut` can be
|
||||||
used to call functions expecting immutable arguments:
|
used to call functions expecting immutable arguments:
|
||||||
|
|
||||||
```v
|
```v ignore
|
||||||
fn f(x St) {...}
|
fn f(x St) {...}
|
||||||
fn g(mut x St) {...}
|
fn g(mut x St) {...}
|
||||||
fn h(shared 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 a `lock c {...}` block `c` behaves like a `mut`,
|
||||||
inside an `rlock c {...}` block like an immutable:
|
inside an `rlock c {...}` block like an immutable:
|
||||||
```v
|
```v ignore
|
||||||
shared c := &St{...}
|
shared c := &St{...}
|
||||||
lock c {
|
lock c {
|
||||||
g(mut 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
|
block. However in simple and obvious cases the necessary lock/unlock
|
||||||
can be generated automatically for `array`/`map` operations:
|
can be generated automatically for `array`/`map` operations:
|
||||||
|
|
||||||
```v
|
```v ignore
|
||||||
shared a []int{...}
|
shared a []int{...}
|
||||||
go h2(shared a)
|
go h2(shared a)
|
||||||
a << 3
|
a << 3
|
||||||
|
Loading…
Reference in New Issue
Block a user