builtin: add more documentation (#13160)

pull/13172/head
jeffmikels 2022-01-14 10:27:38 -05:00 committed by GitHub
parent 4660220f4c
commit 9329b6c8c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 223 additions and 40 deletions

View File

@ -1,10 +1,12 @@
## Description: ## Description:
`builtin` is a module that is implicitly imported by every V program. `builtin` is a module that is implicitly imported by every V program.
It implements the builtin V types `array`, `string`, `map`. It implements the builtin V types `array`, `string`, `map`.
It also implements builtin functions like `println`, `eprintln`, `malloc`, It also implements builtin functions like `println`, `eprintln`, `malloc`,
`panic`, `print_backtrace`. `panic`, `print_backtrace`.
The autogenerated documentation for builtin functions is lacking, The autogenerated documentation for `builtin` functions is lacking, so for these
so for these functions, please refer to the functions, please refer to the
[official V documentation](https://github.com/vlang/v/blob/master/doc/docs.md). [official V documentation](https://github.com/vlang/v/blob/master/doc/docs.md).

View File

@ -5,7 +5,7 @@ module builtin
import strings import strings
// `array` is a struct, used for denoting all array types in V. // array is a struct, used for denoting all array types in V.
// `.data` is a void pointer to the backing heap memory block, // `.data` is a void pointer to the backing heap memory block,
// which avoids using generics and thus without generating extra // which avoids using generics and thus without generating extra
// code for every type. // code for every type.
@ -112,7 +112,9 @@ fn new_array_from_c_array_no_alloc(len int, cap int, elm_size int, c_array voidp
return arr return arr
} }
// Private function. Doubles array capacity if needed. // Private function. Increases the `cap` of an array to the
// required value by copying the data to a new memory location
// (creating a clone) unless `a.cap` is already large enough.
fn (mut a array) ensure_cap(required int) { fn (mut a array) ensure_cap(required int) {
if required <= a.cap { if required <= a.cap {
return return
@ -139,15 +141,17 @@ fn (mut a array) ensure_cap(required int) {
// repeat returns a new array with the given array elements repeated given times. // repeat returns a new array with the given array elements repeated given times.
// `cgen` will replace this with an apropriate call to `repeat_to_depth()` // `cgen` will replace this with an apropriate call to `repeat_to_depth()`
//
// This is a dummy placeholder that will be overridden by `cgen` with an appropriate // This is a dummy placeholder that will be overridden by `cgen` with an appropriate
// call to `repeat_to_depth()`. However the `checker` needs it here. // call to `repeat_to_depth()`. However the `checker` needs it here.
pub fn (a array) repeat(count int) array { pub fn (a array) repeat(count int) array {
return unsafe { a.repeat_to_depth(count, 0) } return unsafe { a.repeat_to_depth(count, 0) }
} }
// version of `repeat()` that handles multi dimensional arrays // repeat_to_depth is an unsafe version of `repeat()` that handles
// `unsafe` to call directly because `depth` is not checked // multi-dimensional arrays.
//
// It is `unsafe` to call directly because `depth` is not checked
[unsafe] [unsafe]
pub fn (a array) repeat_to_depth(count int, depth int) array { pub fn (a array) repeat_to_depth(count int, depth int) array {
if count < 0 { if count < 0 {
@ -176,16 +180,24 @@ pub fn (a array) repeat_to_depth(count int, depth int) array {
return arr return arr
} }
// sort_with_compare sorts array in-place using given `compare` function as comparator. // insert inserts a value in the array at index `i` and increases
pub fn (mut a array) sort_with_compare(callback fn (voidptr, voidptr) int) { // the index of subsequent elements by 1.
$if freestanding { //
panic('sort does not work with -freestanding') // This function is type-aware and can insert items of the same
} $else { // or lower dimensionality as the original array. That is, if
unsafe { vqsort(a.data, usize(a.len), usize(a.element_size), callback) } // the original array is `[]int`, then the insert `val` may be
} // `int` or `[]int`. If the original array is `[][]int`, then `val`
} // may be `[]int` or `[][]int`. Consider the examples.
//
// insert inserts a value in the array at index `i` // Example:
// ```v
// mut a := [1, 2, 4]
// a.insert(2, 3) // a now is [1, 2, 3, 4]
// mut b := [3, 4]
// b.insert(0, [1, 2]) // b now is [1, 2, 3, 4]
// mut c := [[3, 4]]
// c.insert(0, [1, 2]) // c now is [[1, 2], [3, 4]]
// ```
pub fn (mut a array) insert(i int, val voidptr) { pub fn (mut a array) insert(i int, val voidptr) {
$if !no_bounds_checking ? { $if !no_bounds_checking ? {
if i < 0 || i > a.len { if i < 0 || i > a.len {
@ -200,7 +212,9 @@ pub fn (mut a array) insert(i int, val voidptr) {
a.len++ a.len++
} }
// insert_many inserts many values into the array from index `i`. // insert_many inserts many values into the array beginning at `i`.
// NOTE: `array.insert()` already handles inserting multiple values
// use that unless you know you need to use this.
[unsafe] [unsafe]
pub fn (mut a array) insert_many(i int, val voidptr, size int) { pub fn (mut a array) insert_many(i int, val voidptr, size int) {
$if !no_bounds_checking ? { $if !no_bounds_checking ? {
@ -218,23 +232,58 @@ pub fn (mut a array) insert_many(i int, val voidptr, size int) {
a.len += size a.len += size
} }
// prepend prepends one value to the array. // prepend prepends one or more elements to an array.
// Just like `.insert`, `.prepend` can take a single element
// or an array of elements of the same type as the original list.
//
// Example:
// ```v
// mut a := []int{}
// a.prepend(1) // a == [1]
// mut b := [3, 4]
// b.prepend([1, 2]) // b == [1, 2, 3, 4]
//
pub fn (mut a array) prepend(val voidptr) { pub fn (mut a array) prepend(val voidptr) {
a.insert(0, val) a.insert(0, val)
} }
// prepend_many prepends another array to this array. // prepend_many prepends another array to this array.
// NOTE: `.prepend` is probably all you need.
// NOTE: This code is never called in all of vlib
[unsafe] [unsafe]
pub fn (mut a array) prepend_many(val voidptr, size int) { pub fn (mut a array) prepend_many(val voidptr, size int) {
unsafe { a.insert_many(0, val, size) } unsafe { a.insert_many(0, val, size) }
} }
// delete deletes array element at index `i`. // delete deletes array element at index `i`.
// This is exactly the same as calling `.delete_many(i, 1)`.
// NOTE: This function does NOT operate in-place. Internally, it
// creates a copy of the array, skipping over the element at `i`,
// and then points the original variable to the new memory location.
//
// Example:
// ```v
// mut a := ['0', '1', '2', '3', '4', '5']
// a.delete(1) // a is now ['0', '2', '3', '4', '5']
// ```
pub fn (mut a array) delete(i int) { pub fn (mut a array) delete(i int) {
a.delete_many(i, 1) a.delete_many(i, 1)
} }
// delete_many deletes `size` elements beginning with index `i` // delete_many deletes `size` elements beginning with index `i`
// NOTE: This function does NOT operate in-place. Internally, it
// creates a copy of the array, skipping over `size` elements
// starting at `i`, and then points the original variable
// to the new memory location.
//
// Example:
// ```v
// mut a := [1, 2, 3, 4, 5, 6, 7, 8, 9]
// b := a[..9] // creates a `slice` of `a`, not a clone
// a.delete_many(4, 3) // replaces `a` with a modified clone
// dump(a) // a: [1, 2, 3, 4, 8, 9] // `a` is now different
// dump(b) // b: [1, 2, 3, 4, 5, 6, 7, 8, 9] // `b` is still the same
// ```
pub fn (mut a array) delete_many(i int, size int) { pub fn (mut a array) delete_many(i int, size int) {
$if !no_bounds_checking ? { $if !no_bounds_checking ? {
if i < 0 || i + size > a.len { if i < 0 || i + size > a.len {
@ -263,12 +312,15 @@ pub fn (mut a array) delete_many(i int, size int) {
} }
// clear clears the array without deallocating the allocated data. // clear clears the array without deallocating the allocated data.
// It does it by setting the array length to `0`
// Example: a.clear() // `a.len` is now 0
pub fn (mut a array) clear() { pub fn (mut a array) clear() {
a.len = 0 a.len = 0
} }
// trim trims the array length to "index" without modifying the allocated data. If "index" is greater // trim trims the array length to `index` without modifying the allocated data.
// than len nothing will be changed. // If `index` is greater than `len` nothing will be changed.
// Example: a.trim(3) // `a.len` is now <= 3
pub fn (mut a array) trim(index int) { pub fn (mut a array) trim(index int) {
if index < a.len { if index < a.len {
a.len = index a.len = index
@ -305,7 +357,10 @@ fn (a array) get_with_check(i int) voidptr {
} }
} }
// first returns the first element of the array. // first returns the first element of the `array`.
// If the `array` is empty, this will panic.
// However, `a[0]` returns an error object
// so it can be handled with an `or` block.
pub fn (a array) first() voidptr { pub fn (a array) first() voidptr {
$if !no_bounds_checking ? { $if !no_bounds_checking ? {
if a.len == 0 { if a.len == 0 {
@ -315,7 +370,8 @@ pub fn (a array) first() voidptr {
return a.data return a.data
} }
// last returns the last element of the array. // last returns the last element of the `array`.
// If the `array` is empty, this will panic.
pub fn (a array) last() voidptr { pub fn (a array) last() voidptr {
$if !no_bounds_checking ? { $if !no_bounds_checking ? {
if a.len == 0 { if a.len == 0 {
@ -328,6 +384,20 @@ pub fn (a array) last() voidptr {
} }
// pop returns the last element of the array, and removes it. // pop returns the last element of the array, and removes it.
// If the `array` is empty, this will panic.
// NOTE: this function reduces the length of the given array,
// but arrays sliced from this one will not change. They still
// retain their "view" of the underlying memory.
//
// Example:
// ```v
// mut a := [1, 2, 3, 4, 5, 6, 7, 8, 9]
// b := a[..9] // creates a "view" into the same memory
// c := a.pop() // c == 9
// a[1] = 5
// dump(a) // a: [1, 5, 3, 4, 5, 6, 7, 8]
// dump(b) // b: [1, 5, 3, 4, 5, 6, 7, 8, 9]
// ```
pub fn (mut a array) pop() voidptr { pub fn (mut a array) pop() voidptr {
// in a sense, this is the opposite of `a << x` // in a sense, this is the opposite of `a << x`
$if !no_bounds_checking ? { $if !no_bounds_checking ? {
@ -344,6 +414,8 @@ pub fn (mut a array) pop() voidptr {
} }
// delete_last efficiently deletes the last element of the array. // delete_last efficiently deletes the last element of the array.
// It does it simply by reducing the length of the array by 1.
// If the array is empty, this will panic.
pub fn (mut a array) delete_last() { pub fn (mut a array) delete_last() {
// copy pasting code for performance // copy pasting code for performance
$if !no_bounds_checking ? { $if !no_bounds_checking ? {
@ -358,6 +430,11 @@ pub fn (mut a array) delete_last() {
// but starting from the `start` element and ending with the element before // but starting from the `start` element and ending with the element before
// the `end` element of the original array with the length and capacity // the `end` element of the original array with the length and capacity
// set to the number of the elements in the slice. // set to the number of the elements in the slice.
// It will remain tied to the same memory location until the length increases
// (copy on grow) or `.clone()` is called on it.
// If `start` and `end` are invalid this function will panic.
// Alternative: Slices can also be made with [start..end] notation
// Alternative: `.slice_ni()` will always return an array.
fn (a array) slice(start int, _end int) array { fn (a array) slice(start int, _end int) array {
mut end := _end mut end := _end
$if !no_bounds_checking ? { $if !no_bounds_checking ? {
@ -441,7 +518,7 @@ fn (a array) slice2(start int, _end int, end_max bool) array {
return a.slice(start, end) return a.slice(start, end)
} }
// `clone_static_to_depth()` returns an independent copy of a given array. // clone_static_to_depth() returns an independent copy of a given array.
// Unlike `clone_to_depth()` it has a value receiver and is used internally // Unlike `clone_to_depth()` it has a value receiver and is used internally
// for slice-clone expressions like `a[2..4].clone()` and in -autofree generated code. // for slice-clone expressions like `a[2..4].clone()` and in -autofree generated code.
fn (a array) clone_static_to_depth(depth int) array { fn (a array) clone_static_to_depth(depth int) array {
@ -562,7 +639,6 @@ pub fn (a array) reverse() array {
return arr return arr
} }
// pub fn (a []int) free() {
// free frees all memory occupied by the array. // free frees all memory occupied by the array.
[unsafe] [unsafe]
pub fn (a &array) free() { pub fn (a &array) free() {
@ -576,29 +652,105 @@ pub fn (a &array) free() {
unsafe { free(mblock_ptr) } unsafe { free(mblock_ptr) }
} }
// filter creates a new array with all elements that pass the test implemented by the provided function // Some of the following functions have no implementation in V and exist here
// to expose them to the array namespace. Their implementation is compiler
// specific because of their use of `it` and `a < b` expressions.
// Therefore, the implementation is left to the backend.
// filter creates a new array with all elements that pass the test.
// Ignore the function signature. `filter` does not take an actual callback. Rather, it
// takes an `it` expression.
//
// Example: array.filter(it % 2 == 1) // will yield a new array of only odd elements
pub fn (a array) filter(predicate fn (voidptr) bool) array pub fn (a array) filter(predicate fn (voidptr) bool) array
// any tests whether at least one element in the array passes the test implemented by the // any tests whether at least one element in the array passes the test.
// provided function. It returns true if, in the array, it finds an element for which the provided // Ignore the function signature. `any` does not take an actual callback. Rather, it
// function returns true; otherwise it returns false. It doesn't modify the array // takes an `it` expression.
// It returns `true` if it finds an element passing the test. Otherwise,
// it returns `false`. It doesn't modify the array.
//
// Example: array.any(it % 2 == 1) // will return true if any element is odd
pub fn (a array) any(predicate fn (voidptr) bool) bool pub fn (a array) any(predicate fn (voidptr) bool) bool
// all tests whether all elements in the array pass the test implemented by the provided function // all tests whether all elements in the array pass the test
// Ignore the function signature. `all` does not take an actual callback. Rather, it
// takes an `it` expression.
// It returns `false` if any element fails the test. Otherwise,
// it returns `true`. It doesn't modify the array.
//
// Example: array.all(it % 2 == 1) // will return true if every element is odd
pub fn (a array) all(predicate fn (voidptr) bool) bool pub fn (a array) all(predicate fn (voidptr) bool) bool
// map creates a new array populated with the results of calling a provided function // map creates a new array populated with the results of calling a provided function
// on every element in the calling array // on every element in the calling array
pub fn (a array) map(callback fn (voidptr) voidptr) array pub fn (a array) map(callback fn (voidptr) voidptr) array
// sort sorts an array in place in ascending order. // sort sorts an array in place.
// Ignore the function signature. Passing a callback to `.sort` is not supported
// for now. Consider using the `.sort_with_compare` method if you need it.
//
// Instead, a very simple syntax is available to you for custom sorting and more.
//
// Certain array functions (`filter` `any` `all` and `sort`) support a simplified
// domain-specific-language by the backend compiler to make these operations
// more idiomatic to V. These functions are described here, but their implementation
// is compiler specific.
//
// Each function takes a boolean test expression as its single argument.
// These test expressions may use certain 'magic' variables depending on their context:
// - `sort` may use `a` and `b` as pointers to two elements
// giving you direct access to those objects
// - `filter`, `any`, and `all` may use `it` as a pointer to a single element at a time.
//
// Example: array.sort() // will sort the array in ascending order
// Example: array.sort(b < a) // will sort the array in decending order
// Example: array.sort(b.name < a.name) // will sort descending by the .name field
// Example: array.filter(it % 2 == 1) // will yield a new array of only odd elements
// Example: array.any(it.name == 'Bob') // will yield `true` if any element has `.name == 'Bob'`
pub fn (mut a array) sort(callback fn (voidptr, voidptr) int) pub fn (mut a array) sort(callback fn (voidptr, voidptr) int)
// contains determines whether an array includes a certain value among its entries // sort_with_compare sorts array in-place using the results of the
pub fn (a array) contains(val voidptr) bool // given function to determine sort order.
//
// The function should return one of three values:
// - `-1` when `a` should come before `b` ( `a < b` )
// - `1` when `b` should come before `a` ( `b < a` )
// - `0` when the order cannot be determined ( `a == b` )
//
// ### Example:
// ```v
// fn main() {
// mut a := ['hi', '1', '5', '3']
// a.sort_with_compare(fn (a &string, b &string) int {
// if a < b {
// return -1
// }
// if a > b {
// return 1
// }
// return 0
// })
// assert a == ['1', '3', '5', 'hi']
// }
// ```
pub fn (mut a array) sort_with_compare(callback fn (voidptr, voidptr) int) {
$if freestanding {
panic('sort does not work with -freestanding')
} $else {
unsafe { vqsort(a.data, usize(a.len), usize(a.element_size), callback) }
}
}
// contains determines whether an array includes a certain value among its elements
// It will return `true` if the array contains an element with this value.
// It is similar to `.any` but does not take an `it` expression.
//
// Example: [1, 2, 3].contains(4) == false
pub fn (a array) contains(value voidptr) bool
// index returns the first index at which a given element can be found in the array // index returns the first index at which a given element can be found in the array
// or -1 if the value is not found. // or `-1` if the value is not found.
pub fn (a array) index(value voidptr) int pub fn (a array) index(value voidptr) int
[unsafe] [unsafe]
@ -612,8 +764,11 @@ pub fn (mut a []string) free() {
unsafe { (&array(&a)).free() } unsafe { (&array(&a)).free() }
} }
// str returns a string representation of the array of strings // The following functions are type-specific functions that apply
// => '["a", "b", "c"]'. // to arrays of different types in different ways.
// str returns a string representation of an array of strings
// Example: ['a', 'b', 'c'].str() // => "['a', 'b', 'c']".
[manualfree] [manualfree]
pub fn (a []string) str() string { pub fn (a []string) str() string {
mut sb_len := 4 // 2x" + 1x, + 1xspace mut sb_len := 4 // 2x" + 1x, + 1xspace
@ -666,6 +821,7 @@ pub fn (b []byte) hex() string {
// copy copies the `src` byte array elements to the `dst` byte array. // copy copies the `src` byte array elements to the `dst` byte array.
// The number of the elements copied is the minimum of the length of both arrays. // The number of the elements copied is the minimum of the length of both arrays.
// Returns the number of elements copied. // Returns the number of elements copied.
// NOTE: This is not an `array` method. It is a function that takes two arrays of bytes.
// TODO: implement for all types // TODO: implement for all types
pub fn copy(dst []byte, src []byte) int { pub fn copy(dst []byte, src []byte) int {
min := if dst.len < src.len { dst.len } else { src.len } min := if dst.len < src.len { dst.len } else { src.len }
@ -677,6 +833,7 @@ pub fn copy(dst []byte, src []byte) int {
// reduce executes a given reducer function on each element of the array, // reduce executes a given reducer function on each element of the array,
// resulting in a single output value. // resulting in a single output value.
// NOTE: It exists as a method on `[]int` types only
pub fn (a []int) reduce(iter fn (int, int) int, accum_start int) int { pub fn (a []int) reduce(iter fn (int, int) int, accum_start int) int {
mut accum_ := accum_start mut accum_ := accum_start
for i in a { for i in a {
@ -686,11 +843,16 @@ pub fn (a []int) reduce(iter fn (int, int) int, accum_start int) int {
} }
// grow_cap grows the array's capacity by `amount` elements. // grow_cap grows the array's capacity by `amount` elements.
// Internally, it does this by copying the entire array to
// a new memory location (creating a clone).
pub fn (mut a array) grow_cap(amount int) { pub fn (mut a array) grow_cap(amount int) {
a.ensure_cap(a.cap + amount) a.ensure_cap(a.cap + amount)
} }
// grow_len ensures that an array has a.len + amount of length // grow_len ensures that an array has a.len + amount of length
// Internally, it does this by copying the entire array to
// a new memory location (creating a clone) unless the array.cap
// is already large enough.
[unsafe] [unsafe]
pub fn (mut a array) grow_len(amount int) { pub fn (mut a array) grow_len(amount int) {
a.ensure_cap(a.len + amount) a.ensure_cap(a.len + amount)
@ -708,7 +870,8 @@ pub fn (a array) pointers() []voidptr {
return res return res
} }
// voidptr.vbytes() - makes a V []byte structure from a C style memory buffer. NB: the data is reused, NOT copied! // vbytes on`voidptr` makes a V []byte structure from a C style memory buffer.
// NOTE: the data is reused, NOT copied!
[unsafe] [unsafe]
pub fn (data voidptr) vbytes(len int) []byte { pub fn (data voidptr) vbytes(len int) []byte {
res := array{ res := array{
@ -720,7 +883,8 @@ pub fn (data voidptr) vbytes(len int) []byte {
return res return res
} }
// byteptr.vbytes() - makes a V []byte structure from a C style memory buffer. NB: the data is reused, NOT copied! // vbytes on `&byte` makes a V []byte structure from a C style memory buffer.
// NOTE: the data is reused, NOT copied!
[unsafe] [unsafe]
pub fn (data &byte) vbytes(len int) []byte { pub fn (data &byte) vbytes(len int) []byte {
return unsafe { voidptr(data).vbytes(len) } return unsafe { voidptr(data).vbytes(len) }

View File

@ -843,6 +843,20 @@ fn test_sort() {
assert users[2].name == 'Peter' assert users[2].name == 'Peter'
} }
fn test_sort_with_compare() {
mut a := ['hi', '1', '5', '3']
a.sort_with_compare(fn (a &string, b &string) int {
if a < b {
return -1
}
if a > b {
return 1
}
return 0
})
assert a == ['1', '3', '5', 'hi']
}
fn test_rune_sort() { fn test_rune_sort() {
mut bs := [`f`, `e`, `d`, `b`, `c`, `a`] mut bs := [`f`, `e`, `d`, `b`, `c`, `a`]
bs.sort() bs.sort()

View File

@ -539,7 +539,7 @@ pub fn (s string) u64() u64 {
return strconv.common_parse_uint(s, 0, 64, false, false) or { 0 } return strconv.common_parse_uint(s, 0, 64, false, false) or { 0 }
} }
// `parse_int` interprets a string s in the given base (0, 2 to 36) and // parse_int interprets a string s in the given base (0, 2 to 36) and
// bit size (0 to 64) and returns the corresponding value i. // bit size (0 to 64) and returns the corresponding value i.
// //
// If the base argument is 0, the true base is implied by the string's // If the base argument is 0, the true base is implied by the string's
@ -560,7 +560,7 @@ pub fn (s string) parse_uint(_base int, _bit_size int) ?u64 {
return strconv.parse_uint(s, _base, _bit_size) return strconv.parse_uint(s, _base, _bit_size)
} }
// `parse_uint` is like `parse_int` but for unsigned numbers // parse_uint is like `parse_int` but for unsigned numbers
// //
// This method directly exposes the `parse_int` function from `strconv` // This method directly exposes the `parse_int` function from `strconv`
// as a method on `string`. For more advanced features, // as a method on `string`. For more advanced features,

View File

@ -1,3 +1,6 @@
## Description: ## Description:
`strings` provides utilities for efficiently processing large strings. `strings` provides utilities for efficiently processing large strings.
If you got here looking for methods available on the `string` struct, those
methods are found in the `builtin` module.