From 9329b6c8c894cf13c9214decf6e9bef3f284c411 Mon Sep 17 00:00:00 2001 From: jeffmikels Date: Fri, 14 Jan 2022 10:27:38 -0500 Subject: [PATCH] builtin: add more documentation (#13160) --- vlib/builtin/README.md | 6 +- vlib/builtin/array.v | 236 ++++++++++++++++++++++++++++++++------ vlib/builtin/array_test.v | 14 +++ vlib/builtin/string.v | 4 +- vlib/strings/README.md | 3 + 5 files changed, 223 insertions(+), 40 deletions(-) diff --git a/vlib/builtin/README.md b/vlib/builtin/README.md index df08b80ab..f64a3c49a 100644 --- a/vlib/builtin/README.md +++ b/vlib/builtin/README.md @@ -1,10 +1,12 @@ ## Description: `builtin` is a module that is implicitly imported by every V program. + It implements the builtin V types `array`, `string`, `map`. + It also implements builtin functions like `println`, `eprintln`, `malloc`, `panic`, `print_backtrace`. -The autogenerated documentation for builtin functions is lacking, -so for these functions, please refer to the +The autogenerated documentation for `builtin` functions is lacking, so for these +functions, please refer to the [official V documentation](https://github.com/vlang/v/blob/master/doc/docs.md). diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index 10f21c693..260367c90 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -5,7 +5,7 @@ module builtin 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, // which avoids using generics and thus without generating extra // 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 } -// 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) { if required <= a.cap { 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. // `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 // call to `repeat_to_depth()`. However the `checker` needs it here. pub fn (a array) repeat(count int) array { return unsafe { a.repeat_to_depth(count, 0) } } -// version of `repeat()` that handles multi dimensional arrays -// `unsafe` to call directly because `depth` is not checked +// repeat_to_depth is an unsafe version of `repeat()` that handles +// multi-dimensional arrays. +// +// It is `unsafe` to call directly because `depth` is not checked [unsafe] pub fn (a array) repeat_to_depth(count int, depth int) array { if count < 0 { @@ -176,16 +180,24 @@ pub fn (a array) repeat_to_depth(count int, depth int) array { return arr } -// sort_with_compare sorts array in-place using given `compare` function as comparator. -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) } - } -} - -// insert inserts a value in the array at index `i` +// insert inserts a value in the array at index `i` and increases +// the index of subsequent elements by 1. +// +// This function is type-aware and can insert items of the same +// or lower dimensionality as the original array. That is, if +// 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. +// +// 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) { $if !no_bounds_checking ? { if i < 0 || i > a.len { @@ -200,7 +212,9 @@ pub fn (mut a array) insert(i int, val voidptr) { 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] pub fn (mut a array) insert_many(i int, val voidptr, size int) { $if !no_bounds_checking ? { @@ -218,23 +232,58 @@ pub fn (mut a array) insert_many(i int, val voidptr, size int) { 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) { a.insert(0, val) } // 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] pub fn (mut a array) prepend_many(val voidptr, size int) { unsafe { a.insert_many(0, val, size) } } // 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) { a.delete_many(i, 1) } // 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) { $if !no_bounds_checking ? { 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. +// It does it by setting the array length to `0` +// Example: a.clear() // `a.len` is now 0 pub fn (mut a array) clear() { a.len = 0 } -// trim trims the array length to "index" without modifying the allocated data. If "index" is greater -// than len nothing will be changed. +// trim trims the array length to `index` without modifying the allocated data. +// 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) { if index < a.len { 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 { $if !no_bounds_checking ? { if a.len == 0 { @@ -315,7 +370,8 @@ pub fn (a array) first() voidptr { 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 { $if !no_bounds_checking ? { 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. +// 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 { // in a sense, this is the opposite of `a << x` $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. +// 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() { // copy pasting code for performance $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 // the `end` element of the original array with the length and capacity // 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 { mut end := _end $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) } -// `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 // for slice-clone expressions like `a[2..4].clone()` and in -autofree generated code. fn (a array) clone_static_to_depth(depth int) array { @@ -562,7 +639,6 @@ pub fn (a array) reverse() array { return arr } -// pub fn (a []int) free() { // free frees all memory occupied by the array. [unsafe] pub fn (a &array) free() { @@ -576,29 +652,105 @@ pub fn (a &array) free() { 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 -// any tests whether at least one element in the array passes the test implemented by the -// provided function. It returns true if, in the array, it finds an element for which the provided -// function returns true; otherwise it returns false. It doesn't modify the array +// any tests whether at least one element in the array passes the test. +// Ignore the function signature. `any` does not take an actual callback. Rather, it +// 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 -// 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 // map creates a new array populated with the results of calling a provided function // on every element in the calling 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) -// contains determines whether an array includes a certain value among its entries -pub fn (a array) contains(val voidptr) bool +// sort_with_compare sorts array in-place using the results of the +// 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 -// or -1 if the value is not found. +// or `-1` if the value is not found. pub fn (a array) index(value voidptr) int [unsafe] @@ -612,8 +764,11 @@ pub fn (mut a []string) free() { unsafe { (&array(&a)).free() } } -// str returns a string representation of the array of strings -// => '["a", "b", "c"]'. +// The following functions are type-specific functions that apply +// 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] pub fn (a []string) str() string { 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. // The number of the elements copied is the minimum of the length of both arrays. // 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 pub fn copy(dst []byte, src []byte) int { 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, // 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 { mut accum_ := accum_start 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. +// 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) { a.ensure_cap(a.cap + amount) } // 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] pub fn (mut a array) grow_len(amount int) { a.ensure_cap(a.len + amount) @@ -708,7 +870,8 @@ pub fn (a array) pointers() []voidptr { 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] pub fn (data voidptr) vbytes(len int) []byte { res := array{ @@ -720,7 +883,8 @@ pub fn (data voidptr) vbytes(len int) []byte { 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] pub fn (data &byte) vbytes(len int) []byte { return unsafe { voidptr(data).vbytes(len) } diff --git a/vlib/builtin/array_test.v b/vlib/builtin/array_test.v index 19a079236..c2a5eac9d 100644 --- a/vlib/builtin/array_test.v +++ b/vlib/builtin/array_test.v @@ -843,6 +843,20 @@ fn test_sort() { 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() { mut bs := [`f`, `e`, `d`, `b`, `c`, `a`] bs.sort() diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index 65119860b..e07b01985 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -539,7 +539,7 @@ pub fn (s string) u64() u64 { 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. // // 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) } -// `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` // as a method on `string`. For more advanced features, diff --git a/vlib/strings/README.md b/vlib/strings/README.md index 9fce34918..779f891a1 100644 --- a/vlib/strings/README.md +++ b/vlib/strings/README.md @@ -1,3 +1,6 @@ ## Description: `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. -- 2.30.2