1
0
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:
Delyan Angelov
2020-11-03 02:04:14 +02:00
committed by GitHub
parent ae241785bf
commit f32c6784e7
4 changed files with 376 additions and 97 deletions

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