From 0fcccf93b0f480429768fac648f42973cded065f Mon Sep 17 00:00:00 2001 From: despiegk Date: Wed, 25 Dec 2024 20:13:02 +0100 Subject: [PATCH] add virt --- aiprompts/ai_core/core_heroscript.md | 204 ++ aiprompts/ai_core/core_params.md | 136 + aiprompts/ai_core/datatypes.md | 340 ++ .../heroscript & params instructions.md | 309 ++ aiprompts/ai_core/osal_os_system_tools.md | 440 +++ aiprompts/ai_core/v_manual.md | 2939 +++++++++++++++++ aiprompts/ai_core/v_manual2.md | 2009 +++++++++++ aiprompts/binencoder.md | 1 + aiprompts/currency.md | 1 + aiprompts/datatypes.md | 340 ++ aiprompts/details/heroscript_internal.md | 79 + aiprompts/docker.md | 1 + aiprompts/gittools.md | 1 + aiprompts/html_parser.md | 124 + aiprompts/httpconnection.md | 1 + aiprompts/io.md | 105 + aiprompts/net.md | 354 ++ aiprompts/osal.md | 1 + aiprompts/ourdb.md | 1 + aiprompts/ourtime.md | 1 + aiprompts/paramsparser.md | 1 + aiprompts/readme.md | 6 + aiprompts/regex.md | 516 +++ aiprompts/smtp.md | 49 + aiprompts/starter/0_start_here.md | 2 +- aiprompts/time instructions.md | 311 ++ aiprompts/ui console chalk.md | 43 + aiprompts/ui console.md | 205 ++ aiprompts/v_manual_advanced.md | 2403 ++++++++++++++ aiprompts/veb.md | 1221 +++++++ aiprompts/veb_assets.md | 215 ++ aiprompts/veb_auth.md | 128 + aiprompts/veb_csrf.md | 257 ++ aiprompts/veb_sse.md | 83 + aiprompts/vlang webserver veb instructions.md | 907 +++++ .../vshell example script instructions.md | 21 + aiprompts/vtemplates.md | 165 + generate.vsh | 4 +- install_herolib.vsh | 2 +- lib/clients/mailclient/mailclient_model.v | 1 - lib/clients/mycelium/.heroscript | 7 + lib/clients/mycelium/mycelium.v | 140 +- lib/clients/mycelium/mycelium_factory_.v | 88 +- lib/clients/mycelium/mycelium_model.v | 31 +- lib/clients/mycelium/readme.md | 141 +- lib/code/generator/installer_client/ask.v | 8 +- lib/code/generator/installer_client/factory.v | 7 + .../generator/installer_client/generate.v | 2 +- lib/code/generator/installer_client/model.v | 7 +- lib/code/generator/installer_client/scanner.v | 2 + .../templates/heroscript_client | 4 +- .../templates/heroscript_installer | 2 +- .../templates/objname_model.vtemplate | 2 +- lib/core/httpconnection/readme.md | 21 + lib/ui/console/question.v | 1 + lib/ui/console/yesno.v | 3 +- lib/virt/cloudhypervisor/chv_factory.v | 117 + lib/virt/docker/docker_build_args.v | 12 + lib/virt/docker/docker_compose.v | 84 + lib/virt/docker/docker_compose_service.v | 141 + lib/virt/docker/docker_container.v | 134 + lib/virt/docker/docker_container_create.v | 49 + lib/virt/docker/docker_engine.v | 259 ++ lib/virt/docker/docker_factory.v | 26 + lib/virt/docker/docker_image.v | 134 + lib/virt/docker/docker_recipe.v | 286 ++ .../docker/docker_recipe_addfile_embedded.v | 65 + lib/virt/docker/docker_recipe_cmd.v | 38 + lib/virt/docker/docker_recipe_code.v | 52 + .../docker/docker_recipe_code_gobuilder.v | 73 + .../docker/docker_recipe_code_rustbuilder.v | 79 + lib/virt/docker/docker_recipe_copy.v | 52 + lib/virt/docker/docker_recipe_download.v | 55 + lib/virt/docker/docker_recipe_entrypoint.v | 39 + lib/virt/docker/docker_recipe_env.v | 29 + lib/virt/docker/docker_recipe_expose.v | 37 + lib/virt/docker/docker_recipe_from.v | 59 + lib/virt/docker/docker_recipe_package.v | 109 + lib/virt/docker/docker_recipe_run.v | 58 + lib/virt/docker/docker_recipe_snippets.v | 91 + lib/virt/docker/docker_recipe_volume.v | 37 + lib/virt/docker/docker_recipe_workdir.v | 30 + lib/virt/docker/docker_recipe_writefile.v | 35 + lib/virt/docker/docker_recipe_zinit.v | 130 + lib/virt/docker/docker_registry.v | 113 + lib/virt/docker/docker_restapi.v | 3 + lib/virt/docker/docker_test.v | 69 + lib/virt/docker/readme.md | 224 ++ lib/virt/herocontainers/builder.v | 242 ++ lib/virt/herocontainers/builder_info.v | 113 + lib/virt/herocontainers/cengine.v | 81 + .../cengine_builder_solutions.v | 141 + lib/virt/herocontainers/cengine_builders.v | 83 + lib/virt/herocontainers/cengine_containers.v | 163 + lib/virt/herocontainers/cengine_images.v | 138 + lib/virt/herocontainers/container.v | 135 + lib/virt/herocontainers/factory.v | 42 + lib/virt/herocontainers/image.v | 35 + lib/virt/herocontainers/readme.md | 72 + lib/virt/hetzner/.heroscript | 8 + lib/virt/hetzner/actions.v | 274 ++ lib/virt/hetzner/actions_key.v | 78 + lib/virt/hetzner/hetzner_factory_.v | 105 + lib/virt/hetzner/hetzner_model.v | 67 + lib/virt/hetzner/prepare.v | 63 + lib/virt/hetzner/readme.md | 144 + lib/virt/lima/containerd.v | 3 + lib/virt/lima/lima_factory.v | 120 + lib/virt/lima/raw/vm.v | 181 + lib/virt/lima/readme.md | 8 + lib/virt/lima/templates/alpine.yaml | 57 + lib/virt/lima/templates/arch.yaml | 41 + lib/virt/lima/templates/containerd.yaml | 37 + lib/virt/lima/templates/ubuntu.yaml | 45 + lib/virt/lima/templates/ubuntucloud.yaml | 46 + lib/virt/lima/vm.v | 92 + lib/virt/lima/vm_new.v | 101 + lib/virt/qemu/qemu_factory.v | 117 + lib/virt/qemu/readme.md | 8 + lib/virt/qemu/templates/alpine.xml | 32 + lib/virt/qemu/templates/arch.yaml | 41 + lib/virt/qemu/templates/ubuntu.yaml | 40 + lib/virt/qemu/vm.v | 92 + lib/virt/qemu/vm_new.v | 75 + lib/virt/runc/factory.v | 68 + lib/virt/runc/model.v | 221 ++ lib/virt/runc/readme.md | 7 + lib/virt/runc/tojson.v | 153 + lib/virt/utils/containers.v | 128 + lib/virt/utils/containers_test.v | 5 + manual/best_practices/scripts/shebang.md | 2 +- 131 files changed, 20710 insertions(+), 106 deletions(-) create mode 100644 aiprompts/ai_core/core_heroscript.md create mode 100644 aiprompts/ai_core/core_params.md create mode 100644 aiprompts/ai_core/datatypes.md create mode 100644 aiprompts/ai_core/heroscript & params instructions.md create mode 100644 aiprompts/ai_core/osal_os_system_tools.md create mode 100644 aiprompts/ai_core/v_manual.md create mode 100644 aiprompts/ai_core/v_manual2.md create mode 120000 aiprompts/binencoder.md create mode 120000 aiprompts/currency.md create mode 100644 aiprompts/datatypes.md create mode 100644 aiprompts/details/heroscript_internal.md create mode 120000 aiprompts/docker.md create mode 120000 aiprompts/gittools.md create mode 100644 aiprompts/html_parser.md create mode 120000 aiprompts/httpconnection.md create mode 100644 aiprompts/io.md create mode 100644 aiprompts/net.md create mode 120000 aiprompts/osal.md create mode 120000 aiprompts/ourdb.md create mode 120000 aiprompts/ourtime.md create mode 120000 aiprompts/paramsparser.md create mode 100644 aiprompts/readme.md create mode 100644 aiprompts/regex.md create mode 100644 aiprompts/smtp.md create mode 100644 aiprompts/time instructions.md create mode 100644 aiprompts/ui console chalk.md create mode 100644 aiprompts/ui console.md create mode 100644 aiprompts/v_manual_advanced.md create mode 100644 aiprompts/veb.md create mode 100644 aiprompts/veb_assets.md create mode 100644 aiprompts/veb_auth.md create mode 100644 aiprompts/veb_csrf.md create mode 100644 aiprompts/veb_sse.md create mode 100644 aiprompts/vlang webserver veb instructions.md create mode 100644 aiprompts/vshell example script instructions.md create mode 100644 aiprompts/vtemplates.md create mode 100644 lib/virt/cloudhypervisor/chv_factory.v create mode 100644 lib/virt/docker/docker_build_args.v create mode 100644 lib/virt/docker/docker_compose.v create mode 100644 lib/virt/docker/docker_compose_service.v create mode 100644 lib/virt/docker/docker_container.v create mode 100644 lib/virt/docker/docker_container_create.v create mode 100644 lib/virt/docker/docker_engine.v create mode 100644 lib/virt/docker/docker_factory.v create mode 100644 lib/virt/docker/docker_image.v create mode 100644 lib/virt/docker/docker_recipe.v create mode 100644 lib/virt/docker/docker_recipe_addfile_embedded.v create mode 100644 lib/virt/docker/docker_recipe_cmd.v create mode 100644 lib/virt/docker/docker_recipe_code.v create mode 100644 lib/virt/docker/docker_recipe_code_gobuilder.v create mode 100644 lib/virt/docker/docker_recipe_code_rustbuilder.v create mode 100644 lib/virt/docker/docker_recipe_copy.v create mode 100644 lib/virt/docker/docker_recipe_download.v create mode 100644 lib/virt/docker/docker_recipe_entrypoint.v create mode 100644 lib/virt/docker/docker_recipe_env.v create mode 100644 lib/virt/docker/docker_recipe_expose.v create mode 100644 lib/virt/docker/docker_recipe_from.v create mode 100644 lib/virt/docker/docker_recipe_package.v create mode 100644 lib/virt/docker/docker_recipe_run.v create mode 100644 lib/virt/docker/docker_recipe_snippets.v create mode 100644 lib/virt/docker/docker_recipe_volume.v create mode 100644 lib/virt/docker/docker_recipe_workdir.v create mode 100644 lib/virt/docker/docker_recipe_writefile.v create mode 100644 lib/virt/docker/docker_recipe_zinit.v create mode 100644 lib/virt/docker/docker_registry.v create mode 100644 lib/virt/docker/docker_restapi.v create mode 100644 lib/virt/docker/docker_test.v create mode 100644 lib/virt/docker/readme.md create mode 100644 lib/virt/herocontainers/builder.v create mode 100644 lib/virt/herocontainers/builder_info.v create mode 100644 lib/virt/herocontainers/cengine.v create mode 100644 lib/virt/herocontainers/cengine_builder_solutions.v create mode 100644 lib/virt/herocontainers/cengine_builders.v create mode 100644 lib/virt/herocontainers/cengine_containers.v create mode 100644 lib/virt/herocontainers/cengine_images.v create mode 100644 lib/virt/herocontainers/container.v create mode 100644 lib/virt/herocontainers/factory.v create mode 100644 lib/virt/herocontainers/image.v create mode 100644 lib/virt/herocontainers/readme.md create mode 100644 lib/virt/hetzner/.heroscript create mode 100644 lib/virt/hetzner/actions.v create mode 100644 lib/virt/hetzner/actions_key.v create mode 100644 lib/virt/hetzner/hetzner_factory_.v create mode 100644 lib/virt/hetzner/hetzner_model.v create mode 100644 lib/virt/hetzner/prepare.v create mode 100644 lib/virt/hetzner/readme.md create mode 100644 lib/virt/lima/containerd.v create mode 100644 lib/virt/lima/lima_factory.v create mode 100644 lib/virt/lima/raw/vm.v create mode 100644 lib/virt/lima/readme.md create mode 100644 lib/virt/lima/templates/alpine.yaml create mode 100644 lib/virt/lima/templates/arch.yaml create mode 100644 lib/virt/lima/templates/containerd.yaml create mode 100644 lib/virt/lima/templates/ubuntu.yaml create mode 100644 lib/virt/lima/templates/ubuntucloud.yaml create mode 100644 lib/virt/lima/vm.v create mode 100644 lib/virt/lima/vm_new.v create mode 100644 lib/virt/qemu/qemu_factory.v create mode 100644 lib/virt/qemu/readme.md create mode 100644 lib/virt/qemu/templates/alpine.xml create mode 100644 lib/virt/qemu/templates/arch.yaml create mode 100644 lib/virt/qemu/templates/ubuntu.yaml create mode 100644 lib/virt/qemu/vm.v create mode 100644 lib/virt/qemu/vm_new.v create mode 100644 lib/virt/runc/factory.v create mode 100644 lib/virt/runc/model.v create mode 100644 lib/virt/runc/readme.md create mode 100644 lib/virt/runc/tojson.v create mode 100644 lib/virt/utils/containers.v create mode 100644 lib/virt/utils/containers_test.v diff --git a/aiprompts/ai_core/core_heroscript.md b/aiprompts/ai_core/core_heroscript.md new file mode 100644 index 00000000..99cd8dcf --- /dev/null +++ b/aiprompts/ai_core/core_heroscript.md @@ -0,0 +1,204 @@ +# instructions how to work with heroscript in vlang + +## heroscript + +Heroscript is our small scripting language which has following structure + +an example of a heroscript is + +```heroscript + +!!mailclient.configure + name: 'myname' + host: 'localhost' + port: 25 + secure: 1 + reset: 1 + description: ' + a description can be multiline + + like this + ' + +``` + +Notice how: +- every action starts with !! + - the first part is the actor, mailclient in this case + - the 2e part is the action name, configure in this case +- multilines are supported see the description field + +## how to process heroscript in Vlang + +- heroscript can be converted to a struct, +- the methods available to get the params are in 'params' section further in this doc + + +```vlang +//the object which will be configured +pub struct mailclient { +pub mut: + name string + host string + port int + secure bool + description string +} + +mut plbook := playbook.new(text: $the_heroscript_from_above)! +play_mailclient(mut plbook)! //see below in vlang block there it all happens + +pub fn play_mailclient(mut plbook playbook.PlayBook) ! { + + //find all actions are !!$actor.$actionname. in this case above the actor is !!mailclient, we check with the fitler if it exists, if not we return + mailclient_actions := plbook.find(filter: 'mailclient.')! + for action in mailclient_actions { + if action.name == "configure"{ + mut p := action.params + mut obj := mailclientScript{ + //INFO: all details about the get methods can be found in 'params get methods' section + name : p.get('name')! //will give error if not exist + homedir : p.get('homedir')! + title : p.get_default('title', 'My Hero DAG')! //uses a default if not set + reset : p.get_default_false('reset') + start : p.get_default_true('start') + colors : p.get_list('colors') + description : p.get_default('description','')! + } + } + + } +} + + +} + + +## params get methods (param getters) + +above in the p.get... + +below you can find the methods which can be used on the params + +```vlang + +exists(key_ string) bool + +//check if arg exist (arg is just a value in the string e.g. red, not value:something) +exists_arg(key_ string) bool + +//see if the kwarg with the key exists if yes return as string trimmed +get(key_ string) !string + +//return the arg with nr, 0 is the first +get_arg(nr int) !string + +//return arg, if the nr is larger than amount of args, will return the defval +get_arg_default(nr int, defval string) !string + +get_default(key string, defval string) !string + +get_default_false(key string) bool + +get_default_true(key string) bool + +get_float(key string) !f64 + +get_float_default(key string, defval f64) !f64 + +get_from_hashmap(key_ string, defval string, hashmap map[string]string) !string + +get_int(key string) !int + +get_int_default(key string, defval int) !int + +//Looks for a list of strings in the parameters. ',' are used as deliminator to list +get_list(key string) ![]string + +get_list_default(key string, def []string) ![]string + +get_list_f32(key string) ![]f32 + +get_list_f32_default(key string, def []f32) []f32 + +get_list_f64(key string) ![]f64 + +get_list_f64_default(key string, def []f64) []f64 + +get_list_i16(key string) ![]i16 + +get_list_i16_default(key string, def []i16) []i16 + +get_list_i64(key string) ![]i64 + +get_list_i64_default(key string, def []i64) []i64 + +get_list_i8(key string) ![]i8 + +get_list_i8_default(key string, def []i8) []i8 + +get_list_int(key string) ![]int + +get_list_int_default(key string, def []int) []int + +get_list_namefix(key string) ![]string + +get_list_namefix_default(key string, def []string) ![]string + +get_list_u16(key string) ![]u16 + +get_list_u16_default(key string, def []u16) []u16 + +get_list_u32(key string) ![]u32 + +get_list_u32_default(key string, def []u32) []u32 + +get_list_u64(key string) ![]u64 + +get_list_u64_default(key string, def []u64) []u64 + +get_list_u8(key string) ![]u8 + +get_list_u8_default(key string, def []u8) []u8 + +get_map() map[string]string + +get_path(key string) !string + +get_path_create(key string) !string + +get_percentage(key string) !f64 + +get_percentage_default(key string, defval string) !f64 + +//convert GB, MB, KB to bytes e.g. 10 GB becomes bytes in u64 +get_storagecapacity_in_bytes(key string) !u64 + +get_storagecapacity_in_bytes_default(key string, defval u64) !u64 + +get_storagecapacity_in_gigabytes(key string) !u64 + +//Get Expiration object from time string input input can be either relative or absolute## Relative time +get_time(key string) !ourtime.OurTime + +get_time_default(key string, defval ourtime.OurTime) !ourtime.OurTime + +get_time_interval(key string) !Duration + +get_timestamp(key string) !Duration + +get_timestamp_default(key string, defval Duration) !Duration + +get_u32(key string) !u32 + +get_u32_default(key string, defval u32) !u32 + +get_u64(key string) !u64 + +get_u64_default(key string, defval u64) !u64 + +get_u8(key string) !u8 + +get_u8_default(key string, defval u8) !u8 + +``` diff --git a/aiprompts/ai_core/core_params.md b/aiprompts/ai_core/core_params.md new file mode 100644 index 00000000..f201874a --- /dev/null +++ b/aiprompts/ai_core/core_params.md @@ -0,0 +1,136 @@ +# how to use params + +works very well in combination with heroscript + +## How to get the paramsparser + +```v +import freeflowuniverse.crystallib.data.paramsparser + +// Create new params from text +params := paramsparser.new("color:red size:'large' priority:1 enable:true")! + +// Or create empty params and add later +mut params := paramsparser.new_params() +params.set("color", "red") +``` + +## Parameter Format + +The parser supports several formats: + +1. Key-value pairs: `key:value` +2. Quoted values: `key:'value with spaces'` +3. Arguments without keys: `arg1 arg2` +4. Comments: `// this is a comment` + +Example: +```v +text := "name:'John Doe' age:30 active:true // user details" +params := paramsparser.new(text)! +``` + +## Getting Values + +The module provides various methods to retrieve values: + +```v +// Get string value +name := params.get("name")! // returns "John Doe" + +// Get with default value +color := params.get_default("color", "blue")! // returns "blue" if color not set + +// Get as integer +age := params.get_int("age")! // returns 30 + +// Get as boolean (true if value is "1", "true", "y", "yes") +is_active := params.get_default_true("active") + +// Get as float +score := params.get_float("score")! + +// Get as percentage (converts "80%" to 0.8) +progress := params.get_percentage("progress")! +``` + +## Type Conversion Methods + +The module supports various type conversions: + +### Basic Types +- `get_int()`: Convert to int32 +- `get_u32()`: Convert to unsigned 32-bit integer +- `get_u64()`: Convert to unsigned 64-bit integer +- `get_u8()`: Convert to unsigned 8-bit integer +- `get_float()`: Convert to 64-bit float +- `get_percentage()`: Convert percentage string to float (e.g., "80%" → 0.8) + +### Boolean Values +- `get_default_true()`: Returns true if value is empty, "1", "true", "y", or "yes" +- `get_default_false()`: Returns false if value is empty, "0", "false", "n", or "no" + +### Lists +The module provides robust support for parsing and converting lists: + +```v +// Basic list parsing +names := params.get_list("users")! // parses ["user1", "user2", "user3"] + +// With default value +tags := params.get_list_default("tags", ["default"])! + +// Lists with type conversion +numbers := params.get_list_int("ids")! // converts each item to int +amounts := params.get_list_f64("prices")! // converts each item to f64 + +// Name-fixed lists (normalizes each item) +clean_names := params.get_list_namefix("categories")! +``` + +Supported list types: +- `get_list()`: String list +- `get_list_u8()`, `get_list_u16()`, `get_list_u32()`, `get_list_u64()`: Unsigned integers +- `get_list_i8()`, `get_list_i16()`, `get_list_int()`, `get_list_i64()`: Signed integers +- `get_list_f32()`, `get_list_f64()`: Floating point numbers + +Each list method has a corresponding `_default` version that accepts a default value. + +Valid list formats: +```v +users: "john, jane,bob" +ids: "1,2,3,4,5" +``` + +### Advanced + +```v +get_map() map[string]string + +get_path(key string) !string + +get_path_create(key string) !string //will create path if it doesnt exist yet + +get_percentage(key string) !f64 + +get_percentage_default(key string, defval string) !f64 + +//convert GB, MB, KB to bytes e.g. 10 GB becomes bytes in u64 +get_storagecapacity_in_bytes(key string) !u64 + +get_storagecapacity_in_bytes_default(key string, defval u64) !u64 + +get_storagecapacity_in_gigabytes(key string) !u64 + +//Get Expiration object from time string input input can be either relative or absolute## Relative time +get_time(key string) !ourtime.OurTime + +get_time_default(key string, defval ourtime.OurTime) !ourtime.OurTime + +get_time_interval(key string) !Duration + +get_timestamp(key string) !Duration + +get_timestamp_default(key string, defval Duration) !Duration + +``` \ No newline at end of file diff --git a/aiprompts/ai_core/datatypes.md b/aiprompts/ai_core/datatypes.md new file mode 100644 index 00000000..a2c4f298 --- /dev/null +++ b/aiprompts/ai_core/datatypes.md @@ -0,0 +1,340 @@ +module datatypes + +# datatypes + +This module provides implementations of less frequently used, but still common data types. + +V's `builtin` module is imported implicitly, and has implementations for arrays, maps and strings. These are good for many applications, but there are a plethora of other useful data structures/containers, like linked lists, priority queues, trees, etc, that allow for algorithms with different time complexities, which may be more suitable for your specific application. + +It is implemented using generics, that you have to specialise for the type of your actual elements. For example: +```v +import datatypes + +mut stack := datatypes.Stack[int]{} +stack.push(1) +println(stack) +``` + +## Currently Implemented Datatypes: + +- [x] Linked list +- [x] Doubly linked list +- [x] Stack (LIFO) +- [x] Queue (FIFO) +- [x] Min heap (priority queue) +- [x] Set +- [x] Quadtree +- [x] Bloom filter +- [ ] ... + + +fn new_bloom_filter[T](hash_func fn (T) u32, table_size int, num_functions int) !&BloomFilter[T] + new_bloom_filter creates a new bloom_filter. `table_size` should be greater than 0, and `num_functions` should be 1~16. +fn new_bloom_filter_fast[T](hash_func fn (T) u32) &BloomFilter[T] + new_bloom_filter_fast creates a new bloom_filter. `table_size` is 16384, and `num_functions` is 4. +fn new_ringbuffer[T](s int) RingBuffer[T] + new_ringbuffer creates an empty ring buffer of size `s`. +fn (mut bst BSTree[T]) insert(value T) bool + insert give the possibility to insert an element in the BST. +fn (bst &BSTree[T]) contains(value T) bool + contains checks if an element with a given `value` is inside the BST. +fn (mut bst BSTree[T]) remove(value T) bool + remove removes an element with `value` from the BST. +fn (bst &BSTree[T]) is_empty() bool + is_empty checks if the BST is empty +fn (bst &BSTree[T]) in_order_traversal() []T + in_order_traversal traverses the BST in order, and returns the result as an array. +fn (bst &BSTree[T]) post_order_traversal() []T + post_order_traversal traverses the BST in post order, and returns the result in an array. +fn (bst &BSTree[T]) pre_order_traversal() []T + pre_order_traversal traverses the BST in pre order, and returns the result as an array. +fn (bst &BSTree[T]) to_left(value T) !T + to_left returns the value of the node to the left of the node with `value` specified if it exists, otherwise the a false value is returned. + + An example of usage can be the following one + ```v + left_value, exist := bst.to_left(10) + ``` +fn (bst &BSTree[T]) to_right(value T) !T + to_right return the value of the element to the right of the node with `value` specified, if exist otherwise, the boolean value is false An example of usage can be the following one + + ```v + left_value, exist := bst.to_right(10) + ``` +fn (bst &BSTree[T]) max() !T + max return the max element inside the BST. Time complexity O(N) if the BST is not balanced +fn (bst &BSTree[T]) min() !T + min return the minimum element in the BST. Time complexity O(N) if the BST is not balanced. +fn (mut b BloomFilter[T]) add(element T) + adds the element to bloom filter. +fn (b &BloomFilter[T]) exists(element T) bool + checks the element is exists. +fn (l &BloomFilter[T]) @union(r &BloomFilter[T]) !&BloomFilter[T] + @union returns the union of the two bloom filters. +fn (l &BloomFilter[T]) intersection(r &BloomFilter[T]) !&BloomFilter[T] + intersection returns the intersection of bloom filters. +fn (list DoublyLinkedList[T]) is_empty() bool + is_empty checks if the linked list is empty +fn (list DoublyLinkedList[T]) len() int + len returns the length of the linked list +fn (list DoublyLinkedList[T]) first() !T + first returns the first element of the linked list +fn (list DoublyLinkedList[T]) last() !T + last returns the last element of the linked list +fn (mut list DoublyLinkedList[T]) push_back(item T) + push_back adds an element to the end of the linked list +fn (mut list DoublyLinkedList[T]) push_front(item T) + push_front adds an element to the beginning of the linked list +fn (mut list DoublyLinkedList[T]) push_many(elements []T, direction Direction) + push_many adds array of elements to the beginning of the linked list +fn (mut list DoublyLinkedList[T]) pop_back() !T + pop_back removes the last element of the linked list +fn (mut list DoublyLinkedList[T]) pop_front() !T + pop_front removes the last element of the linked list +fn (mut list DoublyLinkedList[T]) insert(idx int, item T) ! + insert adds an element to the linked list at the given index +fn (list &DoublyLinkedList[T]) index(item T) !int + index searches the linked list for item and returns the forward index or none if not found. +fn (mut list DoublyLinkedList[T]) delete(idx int) + delete removes index idx from the linked list and is safe to call for any idx. +fn (list DoublyLinkedList[T]) str() string + str returns a string representation of the linked list +fn (list DoublyLinkedList[T]) array() []T + array returns a array representation of the linked list +fn (mut list DoublyLinkedList[T]) next() ?T + next implements the iter interface to use DoublyLinkedList with V's `for x in list {` loop syntax. +fn (mut list DoublyLinkedList[T]) iterator() DoublyListIter[T] + iterator returns a new iterator instance for the `list`. +fn (mut list DoublyLinkedList[T]) back_iterator() DoublyListIterBack[T] + back_iterator returns a new backwards iterator instance for the `list`. +fn (mut iter DoublyListIterBack[T]) next() ?T + next returns *the previous* element of the list, or `none` when the start of the list is reached. It is called by V's `for x in iter{` on each iteration. +fn (mut iter DoublyListIter[T]) next() ?T + next returns *the next* element of the list, or `none` when the end of the list is reached. It is called by V's `for x in iter{` on each iteration. +fn (list LinkedList[T]) is_empty() bool + is_empty checks if the linked list is empty +fn (list LinkedList[T]) len() int + len returns the length of the linked list +fn (list LinkedList[T]) first() !T + first returns the first element of the linked list +fn (list LinkedList[T]) last() !T + last returns the last element of the linked list +fn (list LinkedList[T]) index(idx int) !T + index returns the element at the given index of the linked list +fn (mut list LinkedList[T]) push(item T) + push adds an element to the end of the linked list +fn (mut list LinkedList[T]) push_many(elements []T) + push adds an array of elements to the end of the linked list +fn (mut list LinkedList[T]) pop() !T + pop removes the last element of the linked list +fn (mut list LinkedList[T]) shift() !T + shift removes the first element of the linked list +fn (mut list LinkedList[T]) insert(idx int, item T) ! + insert adds an element to the linked list at the given index +fn (mut list LinkedList[T]) prepend(item T) + prepend adds an element to the beginning of the linked list (equivalent to insert(0, item)) +fn (list LinkedList[T]) str() string + str returns a string representation of the linked list +fn (list LinkedList[T]) array() []T + array returns a array representation of the linked list +fn (mut list LinkedList[T]) next() ?T + next implements the iteration interface to use LinkedList with V's `for` loop syntax. +fn (mut list LinkedList[T]) iterator() ListIter[T] + iterator returns a new iterator instance for the `list`. +fn (mut iter ListIter[T]) next() ?T + next returns the next element of the list, or `none` when the end of the list is reached. It is called by V's `for x in iter{` on each iteration. +fn (mut heap MinHeap[T]) insert(item T) + insert adds an element to the heap. +fn (mut heap MinHeap[T]) insert_many(elements []T) + insert array of elements to the heap. +fn (mut heap MinHeap[T]) pop() !T + pop removes the top-most element from the heap. +fn (heap MinHeap[T]) peek() !T + peek gets the top-most element from the heap without removing it. +fn (heap MinHeap[T]) len() int + len returns the number of elements in the heap. +fn (queue Queue[T]) is_empty() bool + is_empty checks if the queue is empty +fn (queue Queue[T]) len() int + len returns the length of the queue +fn (queue Queue[T]) peek() !T + peek returns the head of the queue (first element added) +fn (queue Queue[T]) last() !T + last returns the tail of the queue (last element added) +fn (queue Queue[T]) index(idx int) !T + index returns the element at the given index of the queue +fn (mut queue Queue[T]) push(item T) + push adds an element to the tail of the queue +fn (mut queue Queue[T]) pop() !T + pop removes the element at the head of the queue and returns it +fn (queue Queue[T]) str() string + str returns a string representation of the queue +fn (queue Queue[T]) array() []T + array returns a array representation of the queue +fn (mut rb RingBuffer[T]) push(element T) ! + push adds an element to the ring buffer. +fn (mut rb RingBuffer[T]) pop() !T + pop returns the oldest element in the buffer. +fn (mut rb RingBuffer[T]) push_many(elements []T) ! + push_many pushes an array to the buffer. +fn (mut rb RingBuffer[T]) pop_many(n u64) ![]T + pop_many returns `n` elements of the buffer starting with the oldest one. +fn (rb RingBuffer[T]) is_empty() bool + is_empty returns `true` if the ring buffer is empty, `false` otherwise. +fn (rb RingBuffer[T]) is_full() bool + is_full returns `true` if the ring buffer is full, `false` otherwise. +fn (rb RingBuffer[T]) capacity() int + capacity returns the capacity of the ring buffer. +fn (mut rb RingBuffer[T]) clear() + clear empties the ring buffer and all pushed elements. +fn (rb RingBuffer[T]) occupied() int + occupied returns the occupied capacity of the buffer. +fn (rb RingBuffer[T]) remaining() int + remaining returns the remaining capacity of the buffer. +fn (set Set[T]) exists(element T) bool + checks the element is exists. +fn (mut set Set[T]) add(element T) + adds the element to set, if it is not present already. +fn (mut set Set[T]) remove(element T) + removes the element from set. +fn (set Set[T]) pick() !T + pick returns an arbitrary element of set, if set is not empty. +fn (mut set Set[T]) rest() ![]T + rest returns the set consisting of all elements except for the arbitrary element. +fn (mut set Set[T]) pop() !T + pop returns an arbitrary element and deleting it from set. +fn (mut set Set[T]) clear() + delete all elements of set. +fn (l Set[T]) == (r Set[T]) bool + == checks whether the two given sets are equal (i.e. contain all and only the same elements). +fn (set Set[T]) is_empty() bool + is_empty checks whether the set is empty or not. +fn (set Set[T]) size() int + size returns the number of elements in the set. +fn (set Set[T]) copy() Set[T] + copy returns a copy of all the elements in the set. +fn (mut set Set[T]) add_all(elements []T) + add_all adds the whole `elements` array to the set +fn (l Set[T]) @union(r Set[T]) Set[T] + @union returns the union of the two sets. +fn (l Set[T]) intersection(r Set[T]) Set[T] + intersection returns the intersection of sets. +fn (l Set[T]) - (r Set[T]) Set[T] + - returns the difference of sets. +fn (l Set[T]) subset(r Set[T]) bool + subset returns true if the set `r` is a subset of the set `l`. +fn (stack Stack[T]) is_empty() bool + is_empty checks if the stack is empty +fn (stack Stack[T]) len() int + len returns the length of the stack +fn (stack Stack[T]) peek() !T + peek returns the top of the stack +fn (mut stack Stack[T]) push(item T) + push adds an element to the top of the stack +fn (mut stack Stack[T]) pop() !T + pop removes the element at the top of the stack and returns it +fn (stack Stack[T]) str() string + str returns a string representation of the stack +fn (stack Stack[T]) array() []T + array returns a array representation of the stack +enum Direction { + front + back +} +struct AABB { +pub mut: + x f64 + y f64 + width f64 + height f64 +} +struct BSTree[T] { +mut: + root &BSTreeNode[T] = unsafe { 0 } +} + Pure Binary Seach Tree implementation + + Pure V implementation of the Binary Search Tree Time complexity of main operation O(log N) Space complexity O(N) +struct DoublyLinkedList[T] { +mut: + head &DoublyListNode[T] = unsafe { 0 } + tail &DoublyListNode[T] = unsafe { 0 } + // Internal iter pointer for allowing safe modification + // of the list while iterating. TODO: use an option + // instead of a pointer to determine it is initialized. + iter &DoublyListIter[T] = unsafe { 0 } + len int +} + DoublyLinkedList[T] represents a generic doubly linked list of elements, each of type T. +struct DoublyListIter[T] { +mut: + node &DoublyListNode[T] = unsafe { 0 } +} + DoublyListIter[T] is an iterator for DoublyLinkedList. It starts from *the start* and moves forwards to *the end* of the list. It can be used with V's `for x in iter {` construct. One list can have multiple independent iterators, pointing to different positions/places in the list. A DoublyListIter iterator instance always traverses the list from *start to finish*. +struct DoublyListIterBack[T] { +mut: + node &DoublyListNode[T] = unsafe { 0 } +} + DoublyListIterBack[T] is an iterator for DoublyLinkedList. It starts from *the end* and moves backwards to *the start* of the list. It can be used with V's `for x in iter {` construct. One list can have multiple independent iterators, pointing to different positions/places in the list. A DoublyListIterBack iterator instance always traverses the list from *finish to start*. +struct LinkedList[T] { +mut: + head &ListNode[T] = unsafe { 0 } + len int + // Internal iter pointer for allowing safe modification + // of the list while iterating. TODO: use an option + // instead of a pointer to determine if it is initialized. + iter &ListIter[T] = unsafe { 0 } +} +struct ListIter[T] { +mut: + node &ListNode[T] = unsafe { 0 } +} + ListIter[T] is an iterator for LinkedList. It can be used with V's `for x in iter {` construct. One list can have multiple independent iterators, pointing to different positions/places in the list. An iterator instance always traverses the list from start to finish. +struct ListNode[T] { +mut: + data T + next &ListNode[T] = unsafe { 0 } +} +struct MinHeap[T] { +mut: + data []T +} + MinHeap is a binary minimum heap data structure. +struct Quadtree { +pub mut: + perimeter AABB + capacity int + depth int + level int + particles []AABB + nodes []Quadtree +} +fn (mut q Quadtree) create(x f64, y f64, width f64, height f64, capacity int, depth int, level int) Quadtree + create returns a new configurable root node for the tree. +fn (mut q Quadtree) insert(p AABB) + insert recursively adds a particle in the correct index of the tree. +fn (mut q Quadtree) retrieve(p AABB) []AABB + retrieve recursively checks if a particle is in a specific index of the tree. +fn (mut q Quadtree) clear() + clear flushes out nodes and particles from the tree. +fn (q Quadtree) get_nodes() []Quadtree + get_nodes recursively returns the subdivisions the tree has. +struct Queue[T] { +mut: + elements LinkedList[T] +} +struct RingBuffer[T] { +mut: + reader int // index of the tail where data is going to be read + writer int // index of the head where data is going to be written + content []T +} + RingBuffer represents a ring buffer also known as a circular buffer. +struct Set[T] { +mut: + elements map[T]u8 +} +struct Stack[T] { +mut: + elements []T +} diff --git a/aiprompts/ai_core/heroscript & params instructions.md b/aiprompts/ai_core/heroscript & params instructions.md new file mode 100644 index 00000000..fea8cefe --- /dev/null +++ b/aiprompts/ai_core/heroscript & params instructions.md @@ -0,0 +1,309 @@ +# how to work with heroscript in vlang + +## heroscript + +Heroscript is our small scripting language which has following structure + +an example of a heroscript is + +```heroscript + +!!dagu.script_define + name: 'test_dag' + homedir:'' + title:'a title' + reset:1 + start:true //trie or 1 is same + colors: 'green,red,purple' //lists are comma separated + description: ' + a description can be multiline + + like this + ' + + +!!dagu.add_step + dag: 'test_dag' + name: 'hello_world' + command: 'echo hello world' + +!!dagu.add_step + dag: 'test_dag' + name: 'last_step' + command: 'echo last step' + + +``` + +Notice how: +- every action starts with !! + - the first part is the actor e.g. dagu in this case + - the 2e part is the action name +- multilines are supported see the description field + +## how to process heroscript in Vlang + +- heroscript can be converted to a struct, +- the methods available to get the params are in 'params' section further in this doc + + +```vlang + +fn test_play_dagu() ! { + mut plbook := playbook.new(text: thetext_from_above)! + play_dagu(mut plbook)! //see below in vlang block there it all happens +} + + +pub fn play_dagu(mut plbook playbook.PlayBook) ! { + + //find all actions are !!$actor.$actionname. in this case above the actor is !!dagu, we check with the fitler if it exists, if not we return + dagu_actions := plbook.find(filter: 'dagu.')! + if dagu_actions.len == 0 { + return + } + play_dagu_basic(mut plbook)! +} + +pub struct DaguScript { +pub mut: + name string + homedir string + title string + reset bool + start bool + colors []string +} + +// play_dagu plays the dagu play commands +pub fn play_dagu_basic(mut plbook playbook.PlayBook) ! { + + //now find the specific ones for dagu.script_define + mut actions := plbook.find(filter: 'dagu.script_define')! + + if actions.len > 0 { + for myaction in actions { + mut p := myaction.params //get the params object from the action object, this can then be processed using the param getters + mut obj := DaguScript{ + //INFO: all details about the get methods can be found in 'params get methods' section + name : p.get('name')! //will give error if not exist + homedir : p.get('homedir')! + title : p.get_default('title', 'My Hero DAG')! //uses a default if not set + reset : p.get_default_false('reset') + start : p.get_default_true('start') + colors : p.get_list('colors') + description : p.get_default('description','')! + } + ... + } + } + + //there can be more actions which will have other filter + +} + +``` + +## params get methods (param getters) + +```vlang + +fn (params &Params) exists(key_ string) bool + +//check if arg exist (arg is just a value in the string e.g. red, not value:something) +fn (params &Params) exists_arg(key_ string) bool + +//see if the kwarg with the key exists if yes return as string trimmed +fn (params &Params) get(key_ string) !string + +//return the arg with nr, 0 is the first +fn (params &Params) get_arg(nr int) !string + +//return arg, if the nr is larger than amount of args, will return the defval +fn (params &Params) get_arg_default(nr int, defval string) !string + +fn (params &Params) get_default(key string, defval string) !string + +fn (params &Params) get_default_false(key string) bool + +fn (params &Params) get_default_true(key string) bool + +fn (params &Params) get_float(key string) !f64 + +fn (params &Params) get_float_default(key string, defval f64) !f64 + +fn (params &Params) get_from_hashmap(key_ string, defval string, hashmap map[string]string) !string + +fn (params &Params) get_int(key string) !int + +fn (params &Params) get_int_default(key string, defval int) !int + +//Looks for a list of strings in the parameters. ',' are used as deliminator to list +fn (params &Params) get_list(key string) ![]string + +fn (params &Params) get_list_default(key string, def []string) ![]string + +fn (params &Params) get_list_f32(key string) ![]f32 + +fn (params &Params) get_list_f32_default(key string, def []f32) []f32 + +fn (params &Params) get_list_f64(key string) ![]f64 + +fn (params &Params) get_list_f64_default(key string, def []f64) []f64 + +fn (params &Params) get_list_i16(key string) ![]i16 + +fn (params &Params) get_list_i16_default(key string, def []i16) []i16 + +fn (params &Params) get_list_i64(key string) ![]i64 + +fn (params &Params) get_list_i64_default(key string, def []i64) []i64 + +fn (params &Params) get_list_i8(key string) ![]i8 + +fn (params &Params) get_list_i8_default(key string, def []i8) []i8 + +fn (params &Params) get_list_int(key string) ![]int + +fn (params &Params) get_list_int_default(key string, def []int) []int + +fn (params &Params) get_list_namefix(key string) ![]string + +fn (params &Params) get_list_namefix_default(key string, def []string) ![]string + +fn (params &Params) get_list_u16(key string) ![]u16 + +fn (params &Params) get_list_u16_default(key string, def []u16) []u16 + +fn (params &Params) get_list_u32(key string) ![]u32 + +fn (params &Params) get_list_u32_default(key string, def []u32) []u32 + +fn (params &Params) get_list_u64(key string) ![]u64 + +fn (params &Params) get_list_u64_default(key string, def []u64) []u64 + +fn (params &Params) get_list_u8(key string) ![]u8 + +fn (params &Params) get_list_u8_default(key string, def []u8) []u8 + +fn (params &Params) get_map() map[string]string + +fn (params &Params) get_path(key string) !string + +fn (params &Params) get_path_create(key string) !string + +fn (params &Params) get_percentage(key string) !f64 + +fn (params &Params) get_percentage_default(key string, defval string) !f64 + +//convert GB, MB, KB to bytes e.g. 10 GB becomes bytes in u64 +fn (params &Params) get_storagecapacity_in_bytes(key string) !u64 + +fn (params &Params) get_storagecapacity_in_bytes_default(key string, defval u64) !u64 + +fn (params &Params) get_storagecapacity_in_gigabytes(key string) !u64 + +//Get Expiration object from time string input input can be either relative or absolute## Relative time +fn (params &Params) get_time(key string) !ourtime.OurTime + +fn (params &Params) get_time_default(key string, defval ourtime.OurTime) !ourtime.OurTime + +fn (params &Params) get_time_interval(key string) !Duration + +fn (params &Params) get_timestamp(key string) !Duration + +fn (params &Params) get_timestamp_default(key string, defval Duration) !Duration + +fn (params &Params) get_u32(key string) !u32 + +fn (params &Params) get_u32_default(key string, defval u32) !u32 + +fn (params &Params) get_u64(key string) !u64 + +fn (params &Params) get_u64_default(key string, defval u64) !u64 + +fn (params &Params) get_u8(key string) !u8 + +fn (params &Params) get_u8_default(key string, defval u8) !u8 + +``` + +## how internally a heroscript gets parsed for params + +- example to show how a heroscript gets parsed in action with params +- params are part of action object + +```heroscript +example text to parse (heroscript) + +id:a1 name6:aaaaa +name:'need to do something 1' +description: + ' + ## markdown works in it + description can be multiline + lets see what happens + + - a + - something else + + ### subtitle + ' + +name2: test +name3: hi +name10:'this is with space' name11:aaa11 + +name4: 'aaa' + +//somecomment +name5: 'aab' +``` + +the params are part of the action and are represented as follow for the above: + +```vlang +Params{ + params: [Param{ + key: 'id' + value: 'a1' + }, Param{ + key: 'name6' + value: 'aaaaa' + }, Param{ + key: 'name' + value: 'need to do something 1' + }, Param{ + key: 'description' + value: '## markdown works in it + + description can be multiline + lets see what happens + + - a + - something else + + ### subtitle + ' + }, Param{ + key: 'name2' + value: 'test' + }, Param{ + key: 'name3' + value: 'hi' + }, Param{ + key: 'name10' + value: 'this is with space' + }, Param{ + key: 'name11' + value: 'aaa11' + }, Param{ + key: 'name4' + value: 'aaa' + }, Param{ + key: 'name5' + value: 'aab' + }] + } +``` \ No newline at end of file diff --git a/aiprompts/ai_core/osal_os_system_tools.md b/aiprompts/ai_core/osal_os_system_tools.md new file mode 100644 index 00000000..355e3b16 --- /dev/null +++ b/aiprompts/ai_core/osal_os_system_tools.md @@ -0,0 +1,440 @@ +# module osal + + +import as + +```vlang +import freeflowuniverse.crystallib.osal + +osal.ping... + +``` + +## ping + +```go +assert ping(address:"338.8.8.8")==.unknownhost +assert ping(address:"8.8.8.8")==.ok +assert ping(address:"18.8.8.8")==.timeout +``` + +will do a panic if its not one of them, an unknown error + +## platform + +```go +if platform()==.osx{ + //do something +} + +pub enum PlatformType { + unknown + osx + ubuntu + alpine +} + +pub enum CPUType { + unknown + intel + arm + intel32 + arm32 +} + +``` + +## process + + +### execute jobs + +```v +mut job2:=osal.exec(cmd:"ls /")? +println(job2) + +//wont die, the result can be found in /tmp/execscripts +mut job:=osal.exec(cmd:"ls dsds",ignore_error:true)? +//this one has an error +println(job) +``` + +All scripts are executed from a file from /tmp/execscripts + +If the script executes well then its removed, so no leftovers, if it fails the script stays in the dir + +### check process logs + +``` +mut pm:=process.processmap_get()? +``` + +info returns like: + +```json +}, freeflowuniverse.crystallib.process.ProcessInfo{ + cpu_perc: 0 + mem_perc: 0 + cmd: 'mc' + pid: 84455 + ppid: 84467 + rss: 3168 + }, freeflowuniverse.crystallib.process.ProcessInfo{ + cpu_perc: 0 + mem_perc: 0 + cmd: 'zsh -Z -g' + pid: 84467 + ppid: 84469 + rss: 1360 + }] +``` + +## other commands + +fn bin_path() !string +fn cmd_add(args_ CmdAddArgs) ! + copy a binary to the right location on the local computer . e.g. is /usr/local/bin on linux . e.g. is ~/hero/bin on osx . will also add the bin location to the path of .zprofile and .zshrc (different per platform) +fn cmd_exists(cmd string) bool +fn cmd_exists_profile(cmd string) bool +fn cmd_path(cmd string) !string + is same as executing which in OS returns path or error +fn cmd_to_script_path(cmd Command) !string + will return temporary path which then can be executed, is a helper function for making script out of command +fn cputype() CPUType +fn cputype_enum_from_string(cpytype string) CPUType + Returns the enum value that matches the provided string for CPUType +fn dir_delete(path string) ! + remove all if it exists +fn dir_ensure(path string) ! + remove all if it exists +fn dir_reset(path string) ! + remove all if it exists and then (re-)create +fn done_delete(key string) ! +fn done_exists(key string) bool +fn done_get(key string) ?string +fn done_get_int(key string) int +fn done_get_str(key string) string +fn done_print() ! +fn done_reset() ! +fn done_set(key string, val string) ! +fn download(args_ DownloadArgs) !pathlib.Path + if name is not specified, then will be the filename part if the last ends in an extension like .md .txt .log .text ... the file will be downloaded +fn env_get(key string) !string + Returns the requested environment variable if it exists or throws an error if it does not +fn env_get_all() map[string]string + Returns all existing environment variables +fn env_get_default(key string, def string) string + Returns the requested environment variable if it exists or returns the provided default value if it does not +fn env_set(args EnvSet) + Sets an environment if it was not set before, it overwrites the enviroment variable if it exists and if overwrite was set to true (default) +fn env_set_all(args EnvSetAll) + Allows to set multiple enviroment variables in one go, if clear_before_set is true all existing environment variables will be unset before the operation, if overwrite_if_exists is set to true it will overwrite all existing enviromnent variables +fn env_unset(key string) + Unsets an environment variable +fn env_unset_all() + Unsets all environment variables +fn exec(cmd Command) !Job + cmd is the cmd to execute can use ' ' and spaces . if \n in cmd it will write it to ext and then execute with bash . if die==false then will just return returncode,out but not return error . if stdout will show stderr and stdout . . if cmd starts with find or ls, will give to bash -c so it can execute . if cmd has no path, path will be found . . Command argument: . + ``` + name string // to give a name to your command, good to see logs... + cmd string + description string + timeout int = 3600 // timeout in sec + stdout bool = true + stdout_log bool = true + raise_error bool = true // if false, will not raise an error but still error report + ignore_error bool // means if error will just exit and not raise, there will be no error reporting + work_folder string // location where cmd will be executed + environment map[string]string // env variables + ignore_error_codes []int + scriptpath string // is the path where the script will be put which is executed + scriptkeep bool // means we don't remove the script + debug bool // if debug will put +ex in the script which is being executed and will make sure script stays + shell bool // means we will execute it in a shell interactive + retry int + interactive bool = true // make sure we run on non interactive way + async bool + runtime RunTime (.bash, .python) + + returns Job: + start time.Time + end time.Time + cmd Command + output []string + error []string + exit_code int + status JobStatus + process os.Process + ``` + return Job . +fn exec_string(cmd Command) !string + cmd is the cmd to execute can use ' ' and spaces if \n in cmd it will write it to ext and then execute with bash if die==false then will just return returncode,out but not return error if stdout will show stderr and stdout + + if cmd starts with find or ls, will give to bash -c so it can execute if cmd has no path, path will be found $... are remplaced by environment arguments TODO:implement + + Command argument: cmd string timeout int = 600 stdout bool = true die bool = true debug bool + + return what needs to be executed can give it to bash -c ... +fn execute_debug(cmd string) !string +fn execute_interactive(cmd string) ! + shortcut to execute a job interactive means in shell +fn execute_ok(cmd string) bool + executes a cmd, if not error return true +fn execute_silent(cmd string) !string + shortcut to execute a job silent +fn execute_stdout(cmd string) !string + shortcut to execute a job to stdout +fn file_read(path string) !string +fn file_write(path string, text string) ! +fn get_logger() log.Logger + Returns a logger object and allows you to specify via environment argument OSAL_LOG_LEVEL the debug level +fn hero_path() !string +fn hostname() !string +fn initname() !string + e.g. systemd, bash, zinit +fn ipaddr_pub_get() !string + Returns the ipaddress as known on the public side is using resolver4.opendns.com +fn is_linux() bool +fn is_linux_arm() bool +fn is_linux_intel() bool +fn is_osx() bool +fn is_osx_arm() bool +fn is_osx_intel() bool +fn is_ubuntu() bool +fn load_env_file(file_path string) ! +fn memdb_exists(key string) bool +fn memdb_get(key string) string +fn memdb_set(key string, val string) +fn package_install(name_ string) ! + install a package will use right commands per platform +fn package_refresh() ! + update the package list +fn ping(args PingArgs) PingResult + if reached in timout result will be True address is e.g. 8.8.8.8 ping means we check if the destination responds +fn platform() PlatformType +fn platform_enum_from_string(platform string) PlatformType +fn process_exists(pid int) bool +fn process_exists_byname(name string) !bool +fn process_kill_recursive(args ProcessKillArgs) ! + kill process and all the ones underneith +fn processinfo_children(pid int) !ProcessMap + get all children of 1 process +fn processinfo_get(pid int) !ProcessInfo + get process info from 1 specific process returns + ``` + pub struct ProcessInfo { + pub mut: + cpu_perc f32 + mem_perc f32 + cmd string + pid int + ppid int + //resident memory + rss int + } + ``` +fn processinfo_get_byname(name string) ![]ProcessInfo +fn processinfo_with_children(pid int) !ProcessMap + return the process and its children +fn processmap_get() !ProcessMap + make sure to use new first, so that the connection has been initted then you can get it everywhere +fn profile_path() string +fn profile_path_add(args ProfilePathAddArgs) ! + add the following path to a profile +fn profile_path_add_hero() !string +fn profile_path_source() string + return the source statement if the profile exists +fn profile_path_source_and() string + return source $path && . or empty if it doesn't exist +fn sleep(duration int) + sleep in seconds +fn tcp_port_test(args TcpPortTestArgs) bool + test if a tcp port answers + ``` + address string //192.168.8.8 + port int = 22 + timeout u16 = 2000 // total time in milliseconds to keep on trying + ``` +fn user_add(args UserArgs) !int + add's a user if the user does not exist yet +fn user_exists(username string) bool +fn user_id_get(username string) !int +fn usr_local_path() !string + /usr/local on linux, ${os.home_dir()}/hero on osx +fn whoami() !string +fn write_flags[T](options T) string +enum CPUType { + unknown + intel + arm + intel32 + arm32 +} +enum ErrorType { + exec + timeout + args +} +enum JobStatus { + init + running + error_exec + error_timeout + error_args + done +} +enum PMState { + init + ok + old +} +enum PingResult { + ok + timeout // timeout from ping + unknownhost // means we don't know the hostname its a dns issue +} +enum PlatformType { + unknown + osx + ubuntu + alpine + arch + suse +} +enum RunTime { + bash + python + heroscript + herocmd + v +} +struct CmdAddArgs { +pub mut: + cmdname string + source string @[required] // path where the binary is + symlink bool // if rather than copy do a symlink + reset bool // if existing cmd will delete + // bin_repo_url string = 'https://github.com/freeflowuniverse/freeflow_binary' // binary where we put the results +} +struct Command { +pub mut: + name string // to give a name to your command, good to see logs... + cmd string + description string + timeout int = 3600 // timeout in sec + stdout bool = true + stdout_log bool = true + raise_error bool = true // if false, will not raise an error but still error report + ignore_error bool // means if error will just exit and not raise, there will be no error reporting + work_folder string // location where cmd will be executed + environment map[string]string // env variables + ignore_error_codes []int + scriptpath string // is the path where the script will be put which is executed + scriptkeep bool // means we don't remove the script + debug bool // if debug will put +ex in the script which is being executed and will make sure script stays + shell bool // means we will execute it in a shell interactive + retry int + interactive bool = true + async bool + runtime RunTime +} +struct DownloadArgs { +pub mut: + name string // optional (otherwise derived out of filename) + url string + reset bool // will remove + hash string // if hash is known, will verify what hash is + dest string // if specified will copy to that destination + timeout int = 180 + retry int = 3 + minsize_kb u32 = 10 // is always in kb + maxsize_kb u32 + expand_dir string + expand_file string +} +struct EnvSet { +pub mut: + key string @[required] + value string @[required] + overwrite bool = true +} +struct EnvSetAll { +pub mut: + env map[string]string + clear_before_set bool + overwrite_if_exists bool = true +} +struct Job { +pub mut: + start time.Time + end time.Time + cmd Command + output string + error string + exit_code int + status JobStatus + process ?&os.Process @[skip; str: skip] + runnr int // nr of time it runs, is for retry +} +fn (mut job Job) execute_retry() ! + execute the job and wait on result will retry as specified +fn (mut job Job) execute() ! + execute the job, start process, process will not be closed . important you need to close the process later by job.close()! otherwise we get zombie processes +fn (mut job Job) wait() ! + wait till the job finishes or goes in error +fn (mut job Job) process() ! + process (read std.err and std.out of process) +fn (mut job Job) close() ! + will wait & close +struct JobError { + Error +pub mut: + job Job + error_type ErrorType +} +struct PingArgs { +pub mut: + address string @[required] + count u8 = 1 // the ping is successful if it got count amount of replies from the other side + timeout u16 = 1 // the time in which the other side should respond in seconds + retry u8 +} +struct ProcessInfo { +pub mut: + cpu_perc f32 + mem_perc f32 + cmd string + pid int + ppid int // parentpid + // resident memory + rss int +} +fn (mut p ProcessInfo) str() string +struct ProcessKillArgs { +pub mut: + name string + pid int +} +struct ProcessMap { +pub mut: + processes []ProcessInfo + lastscan time.Time + state PMState + pids []int +} +struct ProfilePathAddArgs { +pub mut: + path string @[required] + todelete string // see which one to remove +} +struct TcpPortTestArgs { +pub mut: + address string @[required] // 192.168.8.8 + port int = 22 + timeout u16 = 2000 // total time in milliseconds to keep on trying +} +struct UserArgs { +pub mut: + name string @[required] +} +* \ No newline at end of file diff --git a/aiprompts/ai_core/v_manual.md b/aiprompts/ai_core/v_manual.md new file mode 100644 index 00000000..c2b04fd6 --- /dev/null +++ b/aiprompts/ai_core/v_manual.md @@ -0,0 +1,2939 @@ +# V Documentation + +## Comments + +```v +// This is a single line comment. +/* +This is a multiline comment. + /* It can be nested. */ +*/ +``` + +## Functions + +```v +fn main() { + println(add(77, 33)) + println(sub(100, 50)) +} + +fn add(x int, y int) int { + return x + y +} + +fn sub(x int, y int) int { + return x - y +} +``` + +Again, the type comes after the argument's name. + +Just like in Go and C, functions cannot be overloaded. +This simplifies the code and improves maintainability and readability. + +### Hoisting + +Functions can be used before their declaration: +`add` and `sub` are declared after `main`, but can still be called from `main`. +This is true for all declarations in V and eliminates the need for header files +or thinking about the order of files and declarations. + +### Returning multiple values + +```v +fn foo() (int, int) { + return 2, 3 +} + +a, b := foo() +println(a) // 2 +println(b) // 3 +c, _ := foo() // ignore values using `_` +``` + +## Symbol visibility + +```v +pub fn public_function() { +} + +fn private_function() { +} +``` + +Functions are private (not exported) by default. +To allow other [modules](#module-imports) to use them, prepend `pub`. The same applies +to [structs](#structs), [constants](#constants) and [types](#type-declarations). + +> [!NOTE] +> `pub` can only be used from a named module. +> For information about creating a module, see [Modules](#modules). + +## Variables + +```v +name := 'Bob' +age := 20 +large_number := i64(9999999999) +println(name) +println(age) +println(large_number) +``` + +Variables are declared and initialized with `:=`. This is the only +way to declare variables in V. This means that variables always have an initial +value. + +The variable's type is inferred from the value on the right hand side. +To choose a different type, use type conversion: +the expression `T(v)` converts the value `v` to the +type `T`. + +Unlike most other languages, V only allows defining variables in functions. +By default V does not allow **global variables**. See more [details](#global-variables). + +For consistency across different code bases, all variable and function names +must use the `snake_case` style, as opposed to type names, which must use `PascalCase`. + +### Mutable variables + +```v +mut age := 20 +println(age) +age = 21 +println(age) +``` + +To change the value of the variable use `=`. In V, variables are +immutable by default. +To be able to change the value of the variable, you have to declare it with `mut`. + +Try compiling the program above after removing `mut` from the first line. + +### Initialization vs assignment + +Note the (important) difference between `:=` and `=`. +`:=` is used for declaring and initializing, `=` is used for assigning. + +```v failcompile +fn main() { + age = 21 +} +``` + +This code will not compile, because the variable `age` is not declared. +All variables need to be declared in V. + +```v +fn main() { + age := 21 +} +``` + +The values of multiple variables can be changed in one line. +In this way, their values can be swapped without an intermediary variable. + +```v +mut a := 0 +mut b := 1 +println('${a}, ${b}') // 0, 1 +a, b = b, a +println('${a}, ${b}') // 1, 0 +``` + +### Warnings and declaration errors + +In development mode the compiler will warn you that you haven't used the variable +(you'll get an "unused variable" warning). +In production mode (enabled by passing the `-prod` flag to v – `v -prod foo.v`) +it will not compile at all (like in Go). +```v +fn main() { + a := 10 + // warning: unused variable `a` +} +``` + +To ignore values returned by a function `_` can be used +```v +fn foo() (int, int) { + return 2, 3 +} + +fn main() { + c, _ := foo() + print(c) + // no warning about unused variable returned by foo. +} +``` + +Unlike most languages, variable shadowing is not allowed. Declaring a variable with a name +that is already used in a parent scope will cause a compilation error. +```v failcompile nofmt +fn main() { + a := 10 + if true { + a := 20 // error: redefinition of `a` + } +} +``` +While variable shadowing is not allowed, field shadowing is allowed. +```v +pub struct Dimension { + width int = -1 + height int = -1 +} + +pub struct Test { + Dimension + width int = 100 + // height int +} + +fn main() { + test := Test{} + println('${test.width} ${test.height} ${test.Dimension.width}') // 100 -1 -1 +} +``` +## V Types + +### Primitive types + +```v ignore +bool + +string + +i8 i16 int i64 i128 (soon) +u8 u16 u32 u64 u128 (soon) + +rune // represents a Unicode code point + +f32 f64 + +isize, usize // platform-dependent, the size is how many bytes it takes to reference any location in memory + +voidptr // this one is mostly used for [C interoperability](#v-and-c) + +any // similar to C's void* and Go's interface{} +``` + +> [!NOTE] +> Unlike C and Go, `int` is always a 32 bit integer. + +There is an exception to the rule that all operators +in V must have values of the same type on both sides. A small primitive type +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 + ↗ ↗ + u8 → u16 → u32 → u64 ⬎ + ↘ ↘ ↘ ptr + i8 → i16 → int → i64 ⬏ +``` + +An `int` value for example can be automatically promoted to `f64` +or `i64` but not to `u32`. (`u32` would mean loss of the sign for +negative values). +Promotion from `int` to `f32`, however, is currently done automatically +(but can lead to precision loss for large values). + +Literals like `123` or `4.56` are treated in a special way. They do +not lead to type promotions, however they default to `int` and `f64` +respectively, when their type has to be decided: + +```v nofmt +u := u16(12) +v := 13 + u // v is of type `u16` - no promotion +x := f32(45.6) +y := x + 3.14 // y is of type `f32` - no promotion +a := 75 // a is of type `int` - default for int literal +b := 14.7 // b is of type `f64` - default for float literal +c := u + a // c is of type `int` - automatic promotion of `u`'s value +d := b + x // d is of type `f64` - automatic promotion of `x`'s value +``` + +### Strings + +```v nofmt +name := 'Bob' +assert name.len == 3 // will print 3 +assert name[0] == u8(66) // indexing gives a byte, u8(66) == `B` +assert name[1..3] == 'ob' // slicing gives a string 'ob' + +// escape codes +windows_newline := '\r\n' // escape special characters like in C +assert windows_newline.len == 2 + +// arbitrary bytes can be directly specified using `\x##` notation where `#` is +// a hex digit +aardvark_str := '\x61ardvark' +assert aardvark_str == 'aardvark' +assert '\xc0'[0] == u8(0xc0) + +// or using octal escape `\###` notation where `#` is an octal digit +aardvark_str2 := '\141ardvark' +assert aardvark_str2 == 'aardvark' + +// Unicode can be specified directly as `\u####` where # is a hex digit +// and will be converted internally to its UTF-8 representation +star_str := '\u2605' // ★ +assert star_str == '★' +assert star_str == '\xe2\x98\x85' // UTF-8 can be specified this way too. +``` + +In V, a string is a read-only array of bytes. All Unicode characters are encoded using UTF-8: + +```v +s := 'hello 🌎' // emoji takes 4 bytes +assert s.len == 10 + +arr := s.bytes() // convert `string` to `[]u8` +assert arr.len == 10 + +s2 := arr.bytestr() // convert `[]u8` to `string` +assert s2 == s +``` + +String values are immutable. You cannot mutate elements: + +```v failcompile +mut s := 'hello 🌎' +s[0] = `H` // not allowed +``` + +> error: cannot assign to `s[i]` since V strings are immutable + +Note that indexing a string will produce a `u8` (byte), not a `rune` nor another `string`. Indexes +correspond to _bytes_ in the string, not Unicode code points. If you want to convert the `u8` to a +`string`, use the `.ascii_str()` method on the `u8`: + +```v +country := 'Netherlands' +println(country[0]) // Output: 78 +println(country[0].ascii_str()) // Output: N +``` + +Both single and double quotes can be used to denote strings. For consistency, `vfmt` converts double +quotes to single quotes unless the string contains a single quote character. + +For raw strings, prepend `r`. Escape handling is not done for raw strings: + +```v +s := r'hello\nworld' // the `\n` will be preserved as two characters +println(s) // "hello\nworld" +``` + +Strings can be easily converted to integers: + +```v +s := '42' +n := s.int() // 42 + +// all int literals are supported +assert '0xc3'.int() == 195 +assert '0o10'.int() == 8 +assert '0b1111_0000_1010'.int() == 3850 +assert '-0b1111_0000_1010'.int() == -3850 +``` + +For more advanced `string` processing and conversions, refer to the +[vlib/strconv](https://modules.vlang.io/strconv.html) module. + +#### String interpolation + +Basic interpolation syntax is pretty simple - use `${` before a variable name and `}` after. The +variable will be converted to a string and embedded into the literal: + +```v +name := 'Bob' +println('Hello, ${name}!') // Hello, Bob! +``` + +It also works with fields: `'age = ${user.age}'`. You may also use more complex expressions: +`'can register = ${user.age > 13}'`. + +Format specifiers similar to those in C's `printf()` are also supported. `f`, `g`, `x`, `o`, `b`, +etc. are optional and specify the output format. The compiler takes care of the storage size, so +there is no `hd` or `llu`. + +To use a format specifier, follow this pattern: + +`${varname:[flags][width][.precision][type]}` + +- flags: may be zero or more of the following: `-` to left-align output within the field, `0` to use + `0` as the padding character instead of the default `space` character. + > **Note** + > + > V does not currently support the use of `'` or `#` as format flags, and V supports but + > doesn't need `+` to right-align since that's the default. +- width: may be an integer value describing the minimum width of total field to output. +- precision: an integer value preceded by a `.` will guarantee that many digits after the decimal + point without any insignificant trailing zeros. If displaying insignificant zero's is desired, + append a `f` specifier to the precision value (see examples below). Applies only to float + variables and is ignored for integer variables. +- type: `f` and `F` specify the input is a float and should be rendered as such, `e` and `E` specify + the input is a float and should be rendered as an exponent (partially broken), `g` and `G` specify + the input is a float--the renderer will use floating point notation for small values and exponent + notation for large values, `d` specifies the input is an integer and should be rendered in base-10 + digits, `x` and `X` require an integer and will render it as hexadecimal digits, `o` requires an + integer and will render it as octal digits, `b` requires an integer and will render it as binary + digits, `s` requires a string (almost never used). + + > **Note** + > + > When a numeric type can render alphabetic characters, such as hex strings or special values + > like `infinity`, the lowercase version of the type forces lowercase alphabetics and the + > uppercase version forces uppercase alphabetics. + + > **Note** + > + > In most cases, it's best to leave the format type empty. Floats will be rendered by + > default as `g`, integers will be rendered by default as `d`, and `s` is almost always redundant. + > There are only three cases where specifying a type is recommended: + +- format strings are parsed at compile time, so specifying a type can help detect errors then +- format strings default to using lowercase letters for hex digits and the `e` in exponents. Use a + uppercase type to force the use of uppercase hex digits and an uppercase `E` in exponents. +- format strings are the most convenient way to get hex, binary or octal strings from an integer. + +See +[Format Placeholder Specification](https://en.wikipedia.org/wiki/Printf_format_string#Format_placeholder_specification) +for more information. + +```v +x := 123.4567 +println('[${x:.2}]') // round to two decimal places => [123.46] +println('[${x:10}]') // right-align with spaces on the left => [ 123.457] +println('[${int(x):-10}]') // left-align with spaces on the right => [123 ] +println('[${int(x):010}]') // pad with zeros on the left => [0000000123] +println('[${int(x):b}]') // output as binary => [1111011] +println('[${int(x):o}]') // output as octal => [173] +println('[${int(x):X}]') // output as uppercase hex => [7B] + +println('[${10.0000:.2}]') // remove insignificant 0s at the end => [10] +println('[${10.0000:.2f}]') // do show the 0s at the end, even though they do not change the number => [10.00] +``` + +V also has `r` and `R` switches, which will repeat the string the specified amount of times. + +```v +println('[${'abc':3r}]') // [abcabcabc] +println('[${'abc':3R}]') // [ABCABCABC] +``` + +#### String operators + +```v +name := 'Bob' +bobby := name + 'by' // + is used to concatenate strings +println(bobby) // "Bobby" +mut s := 'hello ' +s += 'world' // `+=` is used to append to a string +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 failcompile +age := 10 +println('age = ' + age) // not allowed +``` + +> error: infix expr: cannot use `int` (right expression) as `string` + +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}') +``` + +See all methods of [string](https://modules.vlang.io/index.html#string) +and related modules [strings](https://modules.vlang.io/strings.html), +[strconv](https://modules.vlang.io/strconv.html). + +### Runes + +A `rune` represents a single Unicode character and is an alias for `u32`. +To denote them, use ` (backticks) : + +```v +rocket := `🚀` +``` + +A `rune` can be converted to a UTF-8 string by using the `.str()` method. + +```v +rocket := `🚀` +assert rocket.str() == '🚀' +``` + +A `rune` can be converted to UTF-8 bytes by using the `.bytes()` method. + +```v +rocket := `🚀` +assert rocket.bytes() == [u8(0xf0), 0x9f, 0x9a, 0x80] +``` + +Hex, Unicode, and Octal escape sequences also work in a `rune` literal: + +```v +assert `\x61` == `a` +assert `\141` == `a` +assert `\u0061` == `a` + +// multibyte literals work too +assert `\u2605` == `★` +assert `\u2605`.bytes() == [u8(0xe2), 0x98, 0x85] +assert `\xe2\x98\x85`.bytes() == [u8(0xe2), 0x98, 0x85] +assert `\342\230\205`.bytes() == [u8(0xe2), 0x98, 0x85] +``` + +Note that `rune` literals use the same escape syntax as strings, but they can only hold one unicode +character. Therefore, if your code does not specify a single Unicode character, you will receive an +error at compile time. + +Also remember that strings are indexed as bytes, not runes, so beware: + +```v +rocket_string := '🚀' +assert rocket_string[0] != `🚀` +assert 'aloha!'[0] == `a` +``` + +A string can be converted to runes by the `.runes()` method. + +```v +hello := 'Hello World 👋' +hello_runes := hello.runes() // [`H`, `e`, `l`, `l`, `o`, ` `, `W`, `o`, `r`, `l`, `d`, ` `, `👋`] +assert hello_runes.string() == hello +``` + +### Numbers + +```v +a := 123 +``` + +This will assign the value of 123 to `a`. By default `a` will have the +type `int`. + +You can also use hexadecimal, binary or octal notation for integer literals: + +```v +a := 0x7B +b := 0b01111011 +c := 0o173 +``` + +All of these will be assigned the same value, 123. They will all have type +`int`, no matter what notation you used. + +V also supports writing numbers with `_` as separator: + +```v +num := 1_000_000 // same as 1000000 +three := 0b0_11 // same as 0b11 +float_num := 3_122.55 // same as 3122.55 +hexa := 0xF_F // same as 255 +oct := 0o17_3 // same as 0o173 +``` + +If you want a different type of integer, you can use casting: + +```v +a := i64(123) +b := u8(42) +c := i16(12345) +``` + +Assigning floating point numbers works the same way: + +```v +f := 1.0 +f1 := f64(3.14) +f2 := f32(3.14) +``` + +If you do not specify the type explicitly, by default float literals +will have the type of `f64`. + +Float literals can also be declared as a power of ten: + +```v +f0 := 42e1 // 420 +f1 := 123e-2 // 1.23 +f2 := 456e+2 // 45600 +``` + +### Arrays + +An array is a collection of data elements of the same type. An array literal is a +list of expressions surrounded by square brackets. An individual element can be +accessed using an *index* expression. Indexes start from `0`: + +```v +mut nums := [1, 2, 3] +println(nums) // `[1, 2, 3]` +println(nums[0]) // `1` +println(nums[1]) // `2` + +nums[1] = 5 +println(nums) // `[1, 5, 3]` +``` + + + +An element can be appended to the end of an array using the push operator `<<`. +It can also append an entire array. + +```v +mut nums := [1, 2, 3] +nums << 4 +println(nums) // "[1, 2, 3, 4]" + +// append array +nums << [5, 6, 7] +println(nums) // "[1, 2, 3, 4, 5, 6, 7]" +``` + +```v +mut names := ['John'] +names << 'Peter' +names << 'Sam' +// names << 10 <-- This will not compile. `names` is an array of strings. +``` + +`val in array` returns true if the array contains `val`. See [`in` operator](#in-operator). + +```v +names := ['John', 'Peter', 'Sam'] +println('Alex' in names) // "false" +``` + +#### Array Fields + +There are two fields that control the "size" of an array: + +* `len`: *length* - the number of pre-allocated and initialized elements in the array +* `cap`: *capacity* - the amount of memory space which has been reserved for elements, + but not initialized or counted as elements. The array can grow up to this size without + being reallocated. Usually, V takes care of this field automatically but there are + cases where the user may want to do manual optimizations (see [below](#array-initialization)). + +```v +mut nums := [1, 2, 3] +println(nums.len) // "3" +println(nums.cap) // "3" or greater +nums = [] // The array is now empty +println(nums.len) // "0" +``` + +`data` is a field (of type `voidptr`) with the address of the first +element. This is for low-level [`unsafe`](#memory-unsafe-code) code. + +> [!NOTE] +> Fields are read-only and can't be modified by the user. + +#### Array Initialization + +The type of an array is determined by the first element: + +* `[1, 2, 3]` is an array of ints (`[]int`). +* `['a', 'b']` is an array of strings (`[]string`). + +The user can explicitly specify the type for the first element: `[u8(16), 32, 64, 128]`. +V arrays are homogeneous (all elements must have the same type). +This means that code like `[1, 'a']` will not compile. + +The above syntax is fine for a small number of known elements but for very large or empty +arrays there is a second initialization syntax: + +```v +mut a := []int{len: 10000, cap: 30000, init: 3} +``` + +This creates an array of 10000 `int` elements that are all initialized with `3`. Memory +space is reserved for 30000 elements. The parameters `len`, `cap` and `init` are optional; +`len` defaults to `0` and `init` to the default initialization of the element type (`0` +for numerical type, `''` for `string`, etc). The run time system makes sure that the +capacity is not smaller than `len` (even if a smaller value is specified explicitly): + +```v +arr := []int{len: 5, init: -1} +// `arr == [-1, -1, -1, -1, -1]`, arr.cap == 5 + +// Declare an empty array: +users := []int{} +``` + +Setting the capacity improves performance of pushing elements to the array +as reallocations can be avoided: + +```v +mut numbers := []int{cap: 1000} +println(numbers.len) // 0 +// Now appending elements won't reallocate +for i in 0 .. 1000 { + numbers << i +} +``` + +> [!NOTE] +> The above code uses a [range `for`](#range-for) statement. + +You can initialize the array by accessing the `index` variable which gives +the index as shown here: + +```v +count := []int{len: 4, init: index} +assert count == [0, 1, 2, 3] + +mut square := []int{len: 6, init: index * index} +// square == [0, 1, 4, 9, 16, 25] +``` + +#### Array Types + +An array can be of these types: + +| Types | Example Definition | +|--------------|--------------------------------------| +| Number | `[]int,[]i64` | +| String | `[]string` | +| Rune | `[]rune` | +| Boolean | `[]bool` | +| Array | `[][]int` | +| Struct | `[]MyStructName` | +| Channel | `[]chan f64` | +| Function | `[]MyFunctionType` `[]fn (int) bool` | +| Interface | `[]MyInterfaceName` | +| Sum Type | `[]MySumTypeName` | +| Generic Type | `[]T` | +| Map | `[]map[string]f64` | +| Enum | `[]MyEnumType` | +| Alias | `[]MyAliasTypeName` | +| Thread | `[]thread int` | +| Reference | `[]&f64` | +| Shared | `[]shared MyStructType` | + +**Example Code:** + +This example uses [Structs](#structs) and [Sum Types](#sum-types) to create an array +which can handle different types (e.g. Points, Lines) of data elements. + +```v +struct Point { + x int + y int +} + +struct Line { + p1 Point + p2 Point +} + +type ObjectSumType = Line | Point + +mut object_list := []ObjectSumType{} +object_list << Point{1, 1} +object_list << Line{ + p1: Point{3, 3} + p2: Point{4, 4} +} +dump(object_list) +/* +object_list: [ObjectSumType(Point{ + x: 1 + y: 1 +}), ObjectSumType(Line{ + p1: Point{ + x: 3 + y: 3 + } + p2: Point{ + x: 4 + y: 4 + } +})] +*/ +``` + +#### Multidimensional Arrays + +Arrays can have more than one dimension. + +2d array example: + +```v +mut a := [][]int{len: 2, init: []int{len: 3}} +a[0][1] = 2 +println(a) // [[0, 2, 0], [0, 0, 0]] +``` + +3d array example: + +```v +mut a := [][][]int{len: 2, init: [][]int{len: 3, init: []int{len: 2}}} +a[0][1][1] = 2 +println(a) // [[[0, 0], [0, 2], [0, 0]], [[0, 0], [0, 0], [0, 0]]] +``` + +#### Array methods + +All arrays can be easily printed with `println(arr)` and converted to a string +with `s := arr.str()`. + +Copying the data from the array is done with `.clone()`: + +```v +nums := [1, 2, 3] +nums_copy := nums.clone() +``` + +Arrays can be efficiently filtered and mapped with the `.filter()` and +`.map()` methods: + +```v +nums := [1, 2, 3, 4, 5, 6] +even := nums.filter(it % 2 == 0) +println(even) // [2, 4, 6] +// filter can accept anonymous functions +even_fn := nums.filter(fn (x int) bool { + return x % 2 == 0 +}) +println(even_fn) +``` + +```v +words := ['hello', 'world'] +upper := words.map(it.to_upper()) +println(upper) // ['HELLO', 'WORLD'] +// map can also accept anonymous functions +upper_fn := words.map(fn (w string) string { + return w.to_upper() +}) +println(upper_fn) // ['HELLO', 'WORLD'] +``` + +`it` is a builtin variable which refers to the element currently being +processed in filter/map methods. + +Additionally, `.any()` and `.all()` can be used to conveniently test +for elements that satisfy a condition. + +```v +nums := [1, 2, 3] +println(nums.any(it == 2)) // true +println(nums.all(it >= 2)) // false +``` + +There are further built-in methods for arrays: + +* `a.repeat(n)` concatenates the array elements `n` times +* `a.insert(i, val)` inserts a new element `val` at index `i` and + shifts all following elements to the right +* `a.insert(i, [3, 4, 5])` inserts several elements +* `a.prepend(val)` inserts a value at the beginning, equivalent to `a.insert(0, val)` +* `a.prepend(arr)` inserts elements of array `arr` at the beginning +* `a.trim(new_len)` truncates the length (if `new_length < a.len`, otherwise does nothing) +* `a.clear()` empties the array without changing `cap` (equivalent to `a.trim(0)`) +* `a.delete_many(start, size)` removes `size` consecutive elements from index `start` + – triggers reallocation +* `a.delete(index)` equivalent to `a.delete_many(index, 1)` +* `a.delete_last()` removes the last element +* `a.first()` equivalent to `a[0]` +* `a.last()` equivalent to `a[a.len - 1]` +* `a.pop()` removes the last element and returns it +* `a.reverse()` makes a new array with the elements of `a` in reverse order +* `a.reverse_in_place()` reverses the order of elements in `a` +* `a.join(joiner)` concatenates an array of strings into one string + using `joiner` string as a separator + +See all methods of [array](https://modules.vlang.io/index.html#array) + +See also [vlib/arrays](https://modules.vlang.io/arrays.html). + +##### Sorting Arrays + +Sorting arrays of all kinds is very simple and intuitive. Special variables `a` and `b` +are used when providing a custom sorting condition. + +```v +mut numbers := [1, 3, 2] +numbers.sort() // 1, 2, 3 +numbers.sort(a > b) // 3, 2, 1 +``` + +```v +struct User { + age int + name string +} + +mut users := [User{21, 'Bob'}, User{20, 'Zarkon'}, User{25, 'Alice'}] +users.sort(a.age < b.age) // sort by User.age int field +users.sort(a.name > b.name) // reverse sort by User.name string field +``` + +V also supports custom sorting, through the `sort_with_compare` array method. +Which expects a comparing function which will define the sort order. +Useful for sorting on multiple fields at the same time by custom sorting rules. +The code below sorts the array ascending on `name` and descending `age`. + +```v +struct User { + age int + name string +} + +mut users := [User{21, 'Bob'}, User{65, 'Bob'}, User{25, 'Alice'}] + +custom_sort_fn := fn (a &User, b &User) int { + // return -1 when a comes before b + // return 0, when both are in same order + // return 1 when b comes before a + if a.name == b.name { + if a.age < b.age { + return 1 + } + if a.age > b.age { + return -1 + } + return 0 + } + if a.name < b.name { + return -1 + } else if a.name > b.name { + return 1 + } + return 0 +} +users.sort_with_compare(custom_sort_fn) +``` + +#### Array Slices + +A slice is a part of a parent array. Initially it refers to the elements +between two indices separated by a `..` operator. The right-side index must +be greater than or equal to the left side index. + +If a right-side index is absent, it is assumed to be the array length. If a +left-side index is absent, it is assumed to be 0. + +```v +nums := [0, 10, 20, 30, 40] +println(nums[1..4]) // [10, 20, 30] +println(nums[..4]) // [0, 10, 20, 30] +println(nums[1..]) // [10, 20, 30, 40] +``` + +In V slices are arrays themselves (they are not distinct types). As a result +all array operations may be performed on them. E.g. they can be pushed onto an +array of the same type: + +```v +array_1 := [3, 5, 4, 7, 6] +mut array_2 := [0, 1] +array_2 << array_1[..3] +println(array_2) // `[0, 1, 3, 5, 4]` +``` + +A slice is always created with the smallest possible capacity `cap == len` (see +[`cap` above](#array-initialization)) no matter what the capacity or length +of the parent array is. As a result it is immediately reallocated and copied to another +memory location when the size increases thus becoming independent from the +parent array (*copy on grow*). In particular pushing elements to a slice +does not alter the parent: + +```v +mut a := [0, 1, 2, 3, 4, 5] + +// Create a slice, that reuses the *same memory* as the parent array +// initially, without doing a new allocation: +mut b := unsafe { a[2..4] } // the contents of `b`, reuses the memory, used by the contents of `a`. + +b[0] = 7 // Note that `b[0]` and `a[2]` refer to *the same element* in memory. +println(a) // `[0, 1, 7, 3, 4, 5]` - changing `b[0]` above, changed `a[2]` too. + +// the content of `b` will get reallocated, to have room for the `9` element: +b << 9 +// The content of `b`, is now reallocated, and fully independent from the content of `a`. + +println(a) // `[0, 1, 7, 3, 4, 5]` - no change, since the content of `b` was reallocated, +// to a larger block, before the appending. + +println(b) // `[7, 3, 9]` - the contents of `b`, after the reallocation, and appending of the `9`. +``` + +Appending to the parent array, may or may not make it independent from its child slices. +The behaviour depends on the *parent's capacity* and is predictable: + +```v +mut a := []int{len: 5, cap: 6, init: 2} +mut b := unsafe { a[1..4] } // the contents of `b` uses part of the same memory, that is used by `a` too + +a << 3 +// still no reallocation of `a`, since `a.len` still fits in `a.cap` +b[2] = 13 // `a[3]` is modified, through the slice `b`. + +a << 4 +// the content of `a` has been reallocated now, and is independent from `b` (`cap` was exceeded by `len`) +b[1] = 3 // no change in `a` + +println(a) // `[2, 2, 2, 13, 2, 3, 4]` +println(b) // `[2, 3, 13]` +``` + +You can call .clone() on the slice, if you *do* want to have an independent copy right away: + +```v +mut a := [0, 1, 2, 3, 4, 5] +mut b := a[2..4].clone() +b[0] = 7 // Note: `b[0]` is NOT referring to `a[2]`, as it would have been, without the `.clone()` +println(a) // [0, 1, 2, 3, 4, 5] +println(b) // [7, 3] +``` + +##### Slices with negative indexes + +V supports array and string slices with negative indexes. +Negative indexing starts from the end of the array towards the start, +for example `-3` is equal to `array.len - 3`. +Negative slices have a different syntax from normal slices, i.e. you need +to add a `gate` between the array name and the square bracket: `a#[..-3]`. +The `gate` specifies that this is a different type of slice and remember that +the result is "locked" inside the array. +The returned slice is always a valid array, though it may be empty: + +```v +a := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +println(a#[-3..]) // [7, 8, 9] +println(a#[-20..]) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +println(a#[-20..-8]) // [0, 1] +println(a#[..-3]) // [0, 1, 2, 3, 4, 5, 6] + +// empty arrays +println(a#[-20..-10]) // [] +println(a#[20..10]) // [] +println(a#[20..30]) // [] +``` + +#### Array method chaining + +You can chain the calls of array methods like `.filter()` and `.map()` and use +the `it` built-in variable to achieve a classic `map/filter` functional paradigm: + +```v +// using filter, map and negatives array slices +files := ['pippo.jpg', '01.bmp', '_v.txt', 'img_02.jpg', 'img_01.JPG'] +filtered := files.filter(it#[-4..].to_lower() == '.jpg').map(it.to_upper()) +// ['PIPPO.JPG', 'IMG_02.JPG', 'IMG_01.JPG'] +``` + +### Fixed size arrays + +V also supports arrays with fixed size. Unlike ordinary arrays, their +length is constant. You cannot append elements to them, nor shrink them. +You can only modify their elements in place. + +However, access to the elements of fixed size arrays is more efficient, +they need less memory than ordinary arrays, and unlike ordinary arrays, +their data is on the stack, so you may want to use them as buffers if you +do not want additional heap allocations. + +Most methods are defined to work on ordinary arrays, not on fixed size arrays. +You can convert a fixed size array to an ordinary array with slicing: + +```v +mut fnums := [3]int{} // fnums is a fixed size array with 3 elements. +fnums[0] = 1 +fnums[1] = 10 +fnums[2] = 100 +println(fnums) // => [1, 10, 100] +println(typeof(fnums).name) // => [3]int + +fnums2 := [1, 10, 100]! // short init syntax that does the same (the syntax will probably change) + +anums := fnums[..] // same as `anums := fnums[0..fnums.len]` +println(anums) // => [1, 10, 100] +println(typeof(anums).name) // => []int +``` + +Note that slicing will cause the data of the fixed size array to be copied to +the newly created ordinary array. + +### Maps + +```v +mut m := map[string]int{} // a map with `string` keys and `int` values +m['one'] = 1 +m['two'] = 2 +println(m['one']) // "1" +println(m['bad_key']) // "0" +println('bad_key' in m) // Use `in` to detect whether such key exists +println(m.keys()) // ['one', 'two'] +m.delete('two') +``` + +Maps can have keys of type string, rune, integer, float or voidptr. + +The whole map can be initialized using this short syntax: + +```v +numbers := { + 'one': 1 + 'two': 2 +} +println(numbers) +``` + +If a key is not found, a zero value is returned by default: + +```v +sm := { + 'abc': 'xyz' +} +val := sm['bad_key'] +println(val) // '' +``` + +```v +intm := { + 1: 1234 + 2: 5678 +} +s := intm[3] +println(s) // 0 +``` + +It's also possible to use an `or {}` block to handle missing keys: + +```v +mm := map[string]int{} +val := mm['bad_key'] or { panic('key not found') } +``` + +You can also check, if a key is present, and get its value, if it was present, in one go: + +```v +m := { + 'abc': 'def' +} +if v := m['abc'] { + println('the map value for that key is: ${v}') +} +``` + +The same option check applies to arrays: + +```v +arr := [1, 2, 3] +large_index := 999 +val := arr[large_index] or { panic('out of bounds') } +println(val) +// you can also do this, if you want to *propagate* the access error: +val2 := arr[333]! +println(val2) +``` + +V also supports nested maps: + +```v +mut m := map[string]map[string]int{} +m['greet'] = { + 'Hello': 1 +} +m['place'] = { + 'world': 2 +} +m['code']['orange'] = 123 +print(m) +``` + +Maps are ordered by insertion, like dictionaries in Python. The order is a +guaranteed language feature. This may change in the future. + +See all methods of +[map](https://modules.vlang.io/index.html#map) +and +[maps](https://modules.vlang.io/maps.html). + +### Map update syntax + +As with structs, V lets you initialise a map with an update applied on top of +another map: + +```v +const base_map = { + 'a': 4 + 'b': 5 +} + +foo := { + ...base_map + 'b': 88 + 'c': 99 +} + +println(foo) // {'a': 4, 'b': 88, 'c': 99} +``` + +This is functionally equivalent to cloning the map and updating it, except that +you don't have to declare a mutable variable: + +```v failcompile +// same as above (except mutable) +mut foo := base_map.clone() +foo['b'] = 88 +foo['c'] = 99 +``` + +## Module imports + +For information about creating a module, see [Modules](#modules). + +Modules can be imported using the `import` keyword: + +```v +import os + +fn main() { + // read text from stdin + name := os.input('Enter your name: ') + println('Hello, ${name}!') +} +``` + +This program can use any public definitions from the `os` module, such +as the `input` function. See the [standard library](https://modules.vlang.io/) +documentation for a list of common modules and their public symbols. + +By default, you have to specify the module prefix every time you call an external function. +This may seem verbose at first, but it makes code much more readable +and easier to understand - it's always clear which function from +which module is being called. This is especially useful in large code bases. + +Cyclic module imports are not allowed, like in Go. + +### Selective imports + +You can also import specific functions and types from modules directly: + +```v +import os { input } + +fn main() { + // read text from stdin + name := input('Enter your name: ') + println('Hello, ${name}!') +} +``` + +> [!NOTE] +> This will import the module as well. Also, this is not allowed for +> constants - they must always be prefixed. + +You can import several specific symbols at once: + +```v +import os { input, user_os } + +name := input('Enter your name: ') +println('Name: ${name}') +current_os := user_os() +println('Your OS is ${current_os}.') +``` + +### Module import aliasing + +Any imported module name can be aliased using the `as` keyword: + +> [!NOTE] +> This example will not compile unless you have created `mymod/sha256/somename.v` +> (submodule names are determined by their path, not by the names of the .v file(s) in them). + +```v failcompile +import crypto.sha256 +import mymod.sha256 as mysha256 + +fn main() { + v_hash := sha256.sum('hi'.bytes()).hex() + my_hash := mysha256.sum('hi'.bytes()).hex() + assert my_hash == v_hash +} +``` + +You cannot alias an imported function or type. +However, you _can_ redeclare a type. + +```v +import time +import math + +type MyTime = time.Time + +fn (mut t MyTime) century() int { + return int(1.0 + math.trunc(f64(t.year) * 0.009999794661191)) +} + +fn main() { + mut my_time := MyTime{ + year: 2020 + month: 12 + day: 25 + } + println(time.new(my_time).utc_string()) + println('Century: ${my_time.century()}') +} +``` + +## Statements & expressions + +### If + +```v +a := 10 +b := 20 +if a < b { + println('${a} < ${b}') +} else if a > b { + println('${a} > ${b}') +} else { + println('${a} == ${b}') +} +``` + +`if` statements are pretty straightforward and similar to most other languages. +Unlike other C-like languages, +there are no parentheses surrounding the condition and the braces are always required. + +#### `If` expressions +Unlike C, V does not have a ternary operator, that would allow you to do: `x = c ? 1 : 2` . +Instead, it has a bit more verbose, but also clearer to read, ability to use `if` as an +expression. The direct translation in V of the ternary construct above, assuming `c` is a +boolean condition, would be: `x = if c { 1 } else { 2 }`. + +Here is another example: +```v +num := 777 +s := if num % 2 == 0 { 'even' } else { 'odd' } +println(s) +// "odd" +``` + +You can use multiple statements in each of the branches of an `if` expression, followed by a final +value, that will become the value of the entire `if` expression, when it takes that branch: +```v +n := arguments().len +x := if n > 2 { + dump(arguments()) + 42 +} else { + println('something else') + 100 +} +dump(x) +``` + +#### `If` unwrapping +Anywhere you can use `or {}`, you can also use "if unwrapping". This binds the unwrapped value +of an expression to a variable when that expression is not none nor an error. + +```v +m := { + 'foo': 'bar' +} + +// handle missing keys +if v := m['foo'] { + println(v) // bar +} else { + println('not found') +} +``` + +```v +fn res() !int { + return 42 +} + +// functions that return a result type +if v := res() { + println(v) +} +``` + +```v +struct User { + name string +} + +arr := [User{'John'}] + +// if unwrapping with assignment of a variable +u_name := if v := arr[0] { + v.name +} else { + 'Unnamed' +} +println(u_name) // John +``` + +#### Type checks and casts + +You can check the current type of a sum type using `is` and its negated form `!is`. + +You can do it either in an `if`: + +```v cgen +struct Abc { + val string +} + +struct Xyz { + foo string +} + +type Alphabet = Abc | Xyz + +x := Alphabet(Abc{'test'}) // sum type +if x is Abc { + // x is automatically cast to Abc and can be used here + println(x) +} +if x !is Abc { + println('Not Abc') +} +``` + +or using `match`: + +```v oksyntax +match x { + Abc { + // x is automatically cast to Abc and can be used here + println(x) + } + Xyz { + // x is automatically cast to Xyz and can be used here + println(x) + } +} +``` + +This works also with struct fields: + +```v +struct MyStruct { + x int +} + +struct MyStruct2 { + y string +} + +type MySumType = MyStruct | MyStruct2 + +struct Abc { + bar MySumType +} + +x := Abc{ + bar: MyStruct{123} // MyStruct will be converted to MySumType type automatically +} +if x.bar is MyStruct { + // x.bar is automatically cast + println(x.bar) +} else if x.bar is MyStruct2 { + new_var := x.bar as MyStruct2 + // ... or you can use `as` to create a type cast an alias manually: + println(new_var) +} +match x.bar { + MyStruct { + // x.bar is automatically cast + println(x.bar) + } + else {} +} +``` + +Mutable variables can change, and doing a cast would be unsafe. +However, sometimes it's useful to type cast despite mutability. +In such cases the developer must mark the expression with the `mut` keyword +to tell the compiler that they know what they're doing. + +It works like this: + +```v oksyntax +mut x := MySumType(MyStruct{123}) +if mut x is MyStruct { + // x is cast to MyStruct even if it's mutable + // without the mut keyword that wouldn't work + println(x) +} +// same with match +match mut x { + MyStruct { + // x is cast to MyStruct even if it's mutable + // without the mut keyword that wouldn't work + println(x) + } +} +``` + +### Match + +```v +os := 'windows' +print('V is running on ') +match os { + 'darwin' { println('macOS.') } + 'linux' { println('Linux.') } + else { println(os) } +} +``` + +A match statement is a shorter way to write a sequence of `if - else` statements. +When a matching branch is found, the following statement block will be run. +The else branch will be run when no other branches match. + +```v +number := 2 +s := match number { + 1 { 'one' } + 2 { 'two' } + else { 'many' } +} +``` + +A match statement can also to be used as an `if - else if - else` alternative: + +```v +match true { + 2 > 4 { println('if') } + 3 == 4 { println('else if') } + 2 == 2 { println('else if2') } + else { println('else') } +} +// 'else if2' should be printed +``` + +or as an `unless` alternative: [unless Ruby](https://www.tutorialspoint.com/ruby/ruby_if_else.htm) + +```v +match false { + 2 > 4 { println('if') } + 3 == 4 { println('else if') } + 2 == 2 { println('else if2') } + else { println('else') } +} +// 'if' should be printed +``` + +A match expression returns the value of the final expression from the matching branch. + +```v +enum Color { + red + blue + green +} + +fn is_red_or_blue(c Color) bool { + return match c { + .red, .blue { true } // comma can be used to test multiple values + .green { false } + } +} +``` + +A match statement can also be used to branch on the variants of an `enum` +by using the shorthand `.variant_here` syntax. An `else` branch is not allowed +when all the branches are exhaustive. + +```v +c := `v` +typ := match c { + `0`...`9` { 'digit' } + `A`...`Z` { 'uppercase' } + `a`...`z` { 'lowercase' } + else { 'other' } +} +println(typ) +// 'lowercase' +``` + +You can also use ranges as `match` patterns. If the value falls within the range +of a branch, that branch will be executed. + +Note that the ranges use `...` (three dots) rather than `..` (two dots). This is +because the range is *inclusive* of the last element, rather than exclusive +(as `..` ranges are). Using `..` in a match branch will throw an error. + +```v +const start = 1 + +const end = 10 + +c := 2 +num := match c { + start...end { + 1000 + } + else { + 0 + } +} +println(num) +// 1000 +``` + +Constants can also be used in the range branch expressions. + +> [!NOTE] +> `match` as an expression is not usable in `for` loop and `if` statements. + +### In operator + +`in` allows to check whether an array or a map contains an element. +To do the opposite, use `!in`. + +```v +nums := [1, 2, 3] +println(1 in nums) // true +println(4 !in nums) // true +``` + +> [!NOTE] +> `in` checks if map contains a key, not a value. + +```v +m := { + 'one': 1 + 'two': 2 +} + +println('one' in m) // true +println('three' !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] { + // ... +} +``` + +V optimizes such expressions, +so both `if` statements above produce the same machine code and no arrays are created. + +### For loop + +V has only one looping keyword: `for`, with several forms. + +#### `for`/`in` + +This is the most common form. You can use it with an array, map or +numeric range. + +##### Array `for` + +```v +numbers := [1, 2, 3, 4, 5] +for num in numbers { + println(num) +} +names := ['Sam', 'Peter'] +for i, name in names { + println('${i}) ${name}') + // Output: 0) Sam + // 1) Peter +} +``` + +The `for value in arr` form is used for going through elements of an array. +If an index is required, an alternative form `for index, value in arr` can be used. + +Note that the value is read-only. +If you need to modify the array while looping, you need to declare the element as mutable: + +```v +mut numbers := [0, 1, 2] +for mut num in numbers { + num++ +} +println(numbers) // [1, 2, 3] +``` + +When an identifier is just a single underscore, it is ignored. + +##### Custom iterators + +Types that implement a `next` method returning an `Option` can be iterated +with a `for` loop. + +```v +struct SquareIterator { + arr []int +mut: + idx int +} + +fn (mut iter SquareIterator) next() ?int { + if iter.idx >= iter.arr.len { + return none + } + defer { + iter.idx++ + } + return iter.arr[iter.idx] * iter.arr[iter.idx] +} + +nums := [1, 2, 3, 4, 5] +iter := SquareIterator{ + arr: nums +} +for squared in iter { + println(squared) +} +``` + +The code above prints: + +``` +1 +4 +9 +16 +25 +``` + +##### Map `for` + +```v +m := { + 'one': 1 + 'two': 2 +} +for key, value in m { + println('${key} -> ${value}') + // Output: one -> 1 + // two -> 2 +} +``` + +Either key or value can be ignored by using a single underscore as the identifier. + +```v +m := { + 'one': 1 + 'two': 2 +} +// iterate over keys +for key, _ in m { + println(key) + // Output: one + // two +} +// iterate over values +for _, value in m { + println(value) + // Output: 1 + // 2 +} +``` + +##### Range `for` + +```v +// Prints '01234' +for i in 0 .. 5 { + print(i) +} +``` + +`low..high` means an *exclusive* range, which represents all values +from `low` up to *but not including* `high`. + +> [!NOTE] +> This exclusive range notation and zero-based indexing follow principles of +logical consistency and error reduction. As Edsger W. Dijkstra outlines in +'Why Numbering Should Start at Zero' +([EWD831](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html)), +zero-based indexing aligns the index with the preceding elements in a sequence, +simplifying handling and minimizing errors, especially with adjacent subsequences. +This logical and efficient approach shapes our language design, emphasizing clarity +and reducing confusion in programming. + +#### Condition `for` + +```v +mut sum := 0 +mut i := 0 +for i <= 100 { + sum += i + i++ +} +println(sum) // "5050" +``` + +This form of the loop is similar to `while` loops in other languages. +The loop will stop iterating once the boolean condition evaluates to false. +Again, there are no parentheses surrounding the condition, and the braces are always required. + +#### Bare `for` + +```v +mut num := 0 +for { + num += 2 + if num >= 10 { + break + } +} +println(num) // "10" +``` + +The condition can be omitted, resulting in an infinite loop. + +#### C `for` + +```v +for i := 0; i < 10; i += 2 { + // Don't print 6 + if i == 6 { + continue + } + println(i) +} +``` + +Finally, there's the traditional C style `for` loop. It's safer than the `while` form +because with the latter it's easy to forget to update the counter and get +stuck in an infinite loop. + +Here `i` doesn't need to be declared with `mut` since it's always going to be mutable by definition. + +#### Labelled break & continue + +`break` and `continue` control the innermost `for` loop by default. +You can also use `break` and `continue` followed by a label name to refer to an outer `for` +loop: + +```v +outer: for i := 4; true; i++ { + println(i) + for { + if i < 7 { + continue outer + } else { + break outer + } + } +} +``` + +The label must immediately precede the outer loop. +The above code prints: + +``` +4 +5 +6 +7 +``` + +### Defer + +A defer statement defers the execution of a block of statements +until the surrounding function returns. + +```v +import os + +fn read_log() { + 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 +} +``` + +If the function returns a value the `defer` block is executed *after* the return +expression is evaluated: + +```v +import os + +enum State { + normal + write_log + return_error +} + +// write log file and return number of bytes written + +fn write_log(s State) !int { + mut f := os.create('log.txt')! + defer { + f.close() + } + if s == .write_log { + // `f.close()` will be called after `f.write()` has been + // executed, but before `write_log()` finally returns the + // number of bytes written to `main()` + return f.writeln('This is a log file') + } else if s == .return_error { + // the file will be closed after the `error()` function + // has returned - so the error message will still report + // it as open + return error('nothing written; file open: ${f.is_opened}') + } + // the file will be closed here, too + return 0 +} + +fn main() { + n := write_log(.return_error) or { + println('Error: ${err}') + 0 + } + println('${n} bytes written') +} +``` + +To access the result of the function inside a `defer` block the `$res()` expression can be used. +`$res()` is only used when a single value is returned, while on multi-return the `$res(idx)` +is parameterized. + +```v ignore +fn (mut app App) auth_middleware() bool { + defer { + if !$res() { + app.response.status_code = 401 + app.response.body = 'Unauthorized' + } + } + header := app.get_header('Authorization') + if header == '' { + return false + } + return true +} + +fn (mut app App) auth_with_user_middleware() (bool, string) { + defer { + if !$res(0) { + app.response.status_code = 401 + app.response.body = 'Unauthorized' + } else { + app.user = $res(1) + } + } + header := app.get_header('Authorization') + if header == '' { + return false, '' + } + return true, 'TestUser' +} +``` + +### Goto + +V allows unconditionally jumping to a label with `goto`. The label name must be contained +within the same function as the `goto` statement. A program may `goto` a label outside +or deeper than the current scope. `goto` allows jumping past variable initialization or +jumping back to code that accesses memory that has already been freed, so it requires +`unsafe`. + +```v ignore +if x { + // ... + if y { + unsafe { + goto my_label + } + } + // ... +} +my_label: +``` + +`goto` should be avoided, particularly when `for` can be used instead. +[Labelled break/continue](#labelled-break--continue) can be used to break out of +a nested loop, and those do not risk violating memory-safety. + +## Structs + +```v +struct Point { + x int + y int +} + +mut p := Point{ + x: 10 + y: 20 +} +println(p.x) // Struct fields are accessed using a dot +// Alternative literal syntax +p = Point{10, 20} +assert p.x == 10 +``` + +### Heap structs + +Structs are allocated on the stack. To allocate a struct on the heap +and get a [reference](#references) 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) +``` + +The type of `p` is `&Point`. It's a [reference](#references) to `Point`. +References are similar to Go pointers and C++ references. + +```v +struct Foo { +mut: + x int +} + +fa := Foo{1} +mut a := fa +a.x = 2 +assert fa.x == 1 +assert a.x == 2 + +// fb := Foo{ 1 } +// mut b := &fb // error: `fb` is immutable, cannot have a mutable reference to it +// b.x = 2 + +mut fc := Foo{1} +mut c := &fc +c.x = 2 +assert fc.x == 2 +assert c.x == 2 +println(fc) // Foo{ x: 2 } +println(c) // &Foo{ x: 2 } // Note `&` prefixed. +``` + +see also [Stack and Heap](#stack-and-heap) + +### Default field values + +```v +struct Foo { + n int // n is 0 by default + s string // s is '' by default + a []int // a is `[]int{}` by default + pos int = -1 // custom default value +} +``` + +All struct fields are zeroed by default during the creation of the struct. +Array and map fields are allocated. +In case of reference value, see [here](#structs-with-reference-fields). + +It's also possible to define custom default values. + +### Required fields + +```v +struct Foo { + n int @[required] +} +``` + +You can mark a struct field with the `[required]` [attribute](#attributes), to tell V that +that field must be initialized when creating an instance of that struct. + +This example will not compile, since the field `n` isn't explicitly initialized: + +```v failcompile +_ = Foo{} +``` + + + +### Short struct literal syntax + +```v +struct Point { + x int + y int +} + +mut p := Point{ + x: 10 + y: 20 +} +p = Point{ + x: 30 + y: 4 +} +assert p.y == 4 +// +// array: first element defines type of array +points := [Point{10, 20}, Point{20, 30}, Point{40, 50}] +println(points) // [Point{x: 10, y: 20}, Point{x: 20, y: 30}, Point{x: 40,y: 50}] +``` + +Omitting the struct name also works for returning a struct literal or passing one +as a function argument. + +### Struct update syntax + +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 User{ + ...u + is_registered: true + } +} + +mut user := User{ + name: 'abc' + age: 23 +} +user = register(user) +println(user) +``` + +### Trailing struct literal arguments + +V doesn't have default function arguments or named arguments, for that trailing struct +literal syntax can be used instead: + +```v +@[params] +struct ButtonConfig { + text string + is_disabled bool + width int = 70 + 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 + } +} + +button := new_button(text: 'Click me', width: 100) +// the height is unset, so it's the default value +assert button.height == 20 +``` + +As you can see, both the struct name and braces can be omitted, instead of: + +```v oksyntax nofmt +new_button(ButtonConfig{text:'Click me', width:100}) +``` + +This only works for functions that take a struct for the last argument. + +> [!NOTE] +> Note the `[params]` tag is used to tell V, that the trailing struct parameter +> can be omitted *entirely*, so that you can write `button := new_button()`. +> Without it, you have to specify *at least* one of the field names, even if it +> has its default value, otherwise the compiler will produce this error message, +> when you call the function with no parameters: +> `error: expected 1 arguments, but got 0`. + +### Access modifiers + +Struct fields are private and immutable by default (making structs immutable as well). +Their access modifiers can be changed with +`pub` and `mut`. In total, there are 5 possible options: + +```v +struct Foo { + a int // private immutable (default) +mut: + b int // private mutable + c int // (you can list multiple fields with the same access modifier) +pub: + d int // public immutable (readonly) +pub mut: + e int // public, but mutable only in parent module +__global: + // (not recommended to use, that's why the 'global' keyword starts with __) + f int // public and mutable both inside and outside parent module +} +``` + +Private fields are available only inside the same [module](#modules), any attempt +to directly access them from another module will cause an error during compilation. +Public immutable fields are readonly everywhere. + +### Anonymous structs + +V supports anonymous structs: structs that don't have to be declared separately +with a struct name. + +```v +struct Book { + author struct { + name string + age int + } + + title string +} + +book := Book{ + author: struct { + name: 'Samantha Black' + age: 24 + } +} +assert book.author.name == 'Samantha Black' +assert book.author.age == 24 +``` + +### Static type methods + +V now supports static type methods like `User.new()`. These are defined on a struct via +`fn [Type name].[function name]` and allow to organize all functions related to a struct: + +```v oksyntax +struct User {} + +fn User.new() User { + return User{} +} + +user := User.new() +``` + +This is an alternative to factory functions like `fn new_user() User {}` and should be used +instead. + +> [!NOTE] +> Note, that these are not constructors, but simple functions. V doesn't have constructors or +> classes. + +### `[noinit]` structs + +V supports `[noinit]` structs, which are structs that cannot be initialised outside the module +they are defined in. They are either meant to be used internally or they can be used externally +through _factory functions_. + +For an example, consider the following source in a directory `sample`: + +```v oksyntax +module sample + +@[noinit] +pub struct Information { +pub: + data string +} + +pub fn new_information(data string) !Information { + if data.len == 0 || data.len > 100 { + return error('data must be between 1 and 100 characters') + } + return Information{ + data: data + } +} +``` + +Note that `new_information` is a _factory_ function. Now when we want to use this struct +outside the module: + +```v okfmt +import sample + +fn main() { + // This doesn't work when the [noinit] attribute is present: + // info := sample.Information{ + // data: 'Sample information.' + // } + + // Use this instead: + info := sample.new_information('Sample information.')! + + println(info) +} +``` + +### Methods + +```v +struct User { + age int +} + +fn (u User) can_register() bool { + return u.age > 16 +} + +user := User{ + age: 10 +} +println(user.can_register()) // "false" +user2 := User{ + age: 20 +} +println(user2.can_register()) // "true" +``` + +V doesn't have classes, but you can define methods on types. +A method is a function with a special receiver argument. +The receiver appears in its own argument list between the `fn` keyword and the method name. +Methods must be in the same module as the receiver type. + +In this example, the `can_register` method has a receiver of type `User` named `u`. +The convention is not to use receiver names like `self` or `this`, +but a short, preferably one letter long, name. + +### Embedded structs + +V supports embedded structs. + +```v +struct Size { +mut: + width int + height int +} + +fn (s &Size) area() int { + return s.width * s.height +} + +struct Button { + Size + title string +} +``` + +With embedding, the struct `Button` will automatically get all the fields and methods from +the struct `Size`, which allows you to do: + +```v oksyntax +mut button := Button{ + title: 'Click me' + height: 2 +} + +button.width = 3 +assert button.area() == 6 +assert button.Size.area() == 6 +print(button) +``` + +output : + +``` +Button{ + Size: Size{ + width: 3 + height: 2 + } + title: 'Click me' +} +``` + +Unlike inheritance, you cannot type cast between structs and embedded structs +(the embedding struct can also have its own fields, and it can also embed multiple structs). + +If you need to access embedded structs directly, use an explicit reference like `button.Size`. + +Conceptually, embedded structs are similar to [mixin](https://en.wikipedia.org/wiki/Mixin)s +in OOP, *NOT* base classes. + +You can also initialize an embedded struct: + +```v oksyntax +mut button := Button{ + Size: Size{ + width: 3 + height: 2 + } +} +``` + +or assign values: + +```v oksyntax +button.Size = Size{ + width: 4 + height: 5 +} +``` + +If multiple embedded structs have methods or fields with the same name, or if methods or fields +with the same name are defined in the struct, you can call methods or assign to variables in +the embedded struct like `button.Size.area()`. +When you do not specify the embedded struct name, the method of the outermost struct will be +targeted. + +## Unions + +Just like structs, unions support embedding. + +```v +struct Rgba32_Component { + r u8 + g u8 + b u8 + a u8 +} + +union Rgba32 { + Rgba32_Component + value u32 +} + +clr1 := Rgba32{ + value: 0x008811FF +} + +clr2 := Rgba32{ + Rgba32_Component: Rgba32_Component{ + a: 128 + } +} + +sz := sizeof(Rgba32) +unsafe { + println('Size: ${sz}B,clr1.b: ${clr1.b},clr2.b: ${clr2.b}') +} +``` + +Output: `Size: 4B, clr1.b: 136, clr2.b: 0` + +Union member access must be performed in an `unsafe` block. + +> [!NOTE] +> Embedded struct arguments are not necessarily stored in the order listed. + +## Functions 2 + +### Immutable function args by default + +In V function arguments are immutable by default, and mutable args have to be +marked on call. + +Since there are also no globals, that means that the return values of the functions, +are a function of their arguments only, and their evaluation has no side effects +(unless the function uses I/O). + +Function arguments are immutable by default, even when [references](#references) are passed. + +> [!NOTE] +> However, V is not a purely functional language. + +There is a compiler flag to enable global variables (`-enable-globals`), but this is +intended for low-level applications like kernels and drivers. + +### Mutable arguments + +It is possible to modify function arguments by declaring them with the keyword `mut`: + +```v +struct User { + name string +mut: + is_registered bool +} + +fn (mut u User) register() { + u.is_registered = true +} + +mut user := User{} +println(user.is_registered) // "false" +user.register() +println(user.is_registered) // "true" +``` + +In this example, the receiver (which is just the first argument) is explicitly marked as mutable, +so `register()` can change the user object. The same works with non-receiver arguments: + +```v +fn multiply_by_2(mut arr []int) { + for i in 0 .. arr.len { + arr[i] *= 2 + } +} + +mut nums := [1, 2, 3] +multiply_by_2(mut nums) +println(nums) +// "[2, 4, 6]" +``` + +Note that you have to add `mut` before `nums` when calling this function. This makes +it clear that the function being called will modify the value. + +It is preferable to return values instead of modifying arguments, +e.g. `user = register(user)` (or `user.register()`) instead of `register(mut user)`. +Modifying arguments should only be done in performance-critical parts of your application +to reduce allocations and copying. + +For this reason V doesn't allow the modification of arguments with primitive types (e.g. integers). +Only more complex types such as arrays and maps may be modified. + +### Variable number of arguments +V supports functions that receive an arbitrary, variable amounts of arguments, denoted with the +`...` prefix. +Below, `a ...int` refers to an arbitrary amount of parameters that will be collected +into an array named `a`. + +```v +fn sum(a ...int) int { + mut total := 0 + for x in a { + total += x + } + return total +} + +println(sum()) // 0 +println(sum(1)) // 1 +println(sum(2, 3)) // 5 +// using array decomposition +a := [2, 3, 4] +println(sum(...a)) // <-- using prefix ... here. output: 9 +b := [5, 6, 7] +println(sum(...b)) // output: 18 +``` + +### Anonymous & higher order functions + +```v +fn sqr(n int) int { + return n * n +} + +fn cube(n int) int { + return n * n * n +} + +fn run(value int, op fn (int) int) int { + return op(value) +} + +fn main() { + // Functions can be passed to other functions + println(run(5, sqr)) // "25" + // Anonymous functions can be declared inside other functions: + double_fn := fn (n int) int { + return n + n + } + println(run(5, double_fn)) // "10" + // Functions can be passed around without assigning them to variables: + res := run(5, fn (n int) int { + return n + n + }) + println(res) // "10" + // You can even have an array/map of functions: + fns := [sqr, cube] + println(fns[0](10)) // "100" + fns_map := { + 'sqr': sqr + 'cube': cube + } + println(fns_map['cube'](2)) // "8" +} +``` + +### Closures + +V supports closures too. +This means that anonymous functions can inherit variables from the scope they were created in. +They must do so explicitly by listing all variables that are inherited. + +```v oksyntax +my_int := 1 +my_closure := fn [my_int] () { + println(my_int) +} +my_closure() // prints 1 +``` + +Inherited variables are copied when the anonymous function is created. +This means that if the original variable is modified after the creation of the function, +the modification won't be reflected in the function. + +```v oksyntax +mut i := 1 +func := fn [i] () int { + return i +} +println(func() == 1) // true +i = 123 +println(func() == 1) // still true +``` + +However, the variable can be modified inside the anonymous function. +The change won't be reflected outside, but will be in the later function calls. + +```v oksyntax +fn new_counter() fn () int { + mut i := 0 + return fn [mut i] () int { + i++ + return i + } +} + +c := new_counter() +println(c()) // 1 +println(c()) // 2 +println(c()) // 3 +``` + +If you need the value to be modified outside the function, use a reference. + +```v oksyntax +mut i := 0 +mut ref := &i +print_counter := fn [ref] () { + println(*ref) +} + +print_counter() // 0 +i = 10 +print_counter() // 10 +``` + +### Parameter evaluation order + +The evaluation order of the parameters of function calls is *NOT* guaranteed. +Take for example the following program: + +```v +fn f(a1 int, a2 int, a3 int) { + dump(a1 + a2 + a3) +} + +fn main() { + f(dump(100), dump(200), dump(300)) +} +``` + +V currently does not guarantee that it will print 100, 200, 300 in that order. +The only guarantee is that 600 (from the body of `f`) will be printed after all of them. + +This *may* change in V 1.0 . + +## References + +```v +struct Foo {} + +fn (foo Foo) bar_method() { + // ... +} + +fn bar_function(foo Foo) { + // ... +} +``` + +If a function argument is immutable (like `foo` in the examples above) +V can pass it either by value or by reference. The compiler will decide, +and the developer doesn't need to think about it. + +You no longer need to remember whether you should pass the struct by value +or by reference. + +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) +} +``` + +`foo` is still immutable and can't be changed. For that, +`(mut foo Foo)` must be used. + +In general, V's references are similar to Go pointers and C++ references. +For example, a generic tree structure definition would look like this: + +```v +struct Node[T] { + val T + left &Node[T] + right &Node[T] +} +``` + +To dereference a reference, use the `*` operator, just like in C. + +## Constants + +```v +const pi = 3.14 +const world = '世界' + +println(pi) +println(world) +``` + +Constants are declared with `const`. They can only be defined +at the module level (outside of functions). +Constant values can never be changed. You can also declare a single +constant separately: + +```v +const e = 2.71828 +``` + +V constants are more flexible than in most languages. You can assign more complex values: + +```v +struct Color { + r int + g int + b int +} + +fn rgb(r int, g int, b int) Color { + return Color{ + r: r + g: g + b: b + } +} + +const numbers = [1, 2, 3] +const red = Color{ + r: 255 + g: 0 + b: 0 +} +// evaluate function call at compile time* +const blue = rgb(0, 0, 255) + +println(numbers) +println(red) +println(blue) +``` + +\* WIP - for now function calls are evaluated at program start-up + +Global variables are not normally allowed, so this can be really useful. + +**Modules** + +Constants can be made public with `pub const`: + +```v oksyntax +module mymodule + +pub const golden_ratio = 1.61803 + +fn calc() { + println(golden_ratio) +} +``` + +The `pub` keyword is only allowed before the `const` keyword and cannot be used inside +a `const ( )` block. + +Outside from module main all constants need to be prefixed with the module name. + +### Required module prefix + +When naming constants, `snake_case` must be used. In order to distinguish consts +from local variables, the full path to consts must be specified. For example, +to access the PI const, full `math.pi` name must be used both outside the `math` +module, and inside it. That restriction is relaxed only for the `main` module +(the one containing your `fn main()`), where you can use the unqualified name of +constants defined there, i.e. `numbers`, rather than `main.numbers`. + +vfmt takes care of this rule, so you can type `println(pi)` inside the `math` module, +and vfmt will automatically update it to `println(math.pi)`. + + + +## Builtin functions + +Some functions are builtin like `println`. Here is the complete list: + +```v ignore +fn print(s string) // prints anything on stdout +fn println(s string) // prints anything and a newline on stdout + +fn eprint(s string) // same as print(), but uses stderr +fn eprintln(s string) // same as println(), but uses stderr + +fn exit(code int) // terminates the program with a custom error code +fn panic(s string) // prints a message and backtraces on stderr, and terminates the program with error code 1 +fn print_backtrace() // prints backtraces on stderr +``` + +> [!NOTE] +> Although the `print` functions take a string, V accepts other printable types too. +> See below for details. + +There is also a special built-in function called [`dump`](#dumping-expressions-at-runtime). + +### println + +`println` is a simple yet powerful builtin function, that can print anything: +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]" +println(User{ name: 'Bob', age: 20 }) // "User{name:'Bob', age:20}" +``` + +See also [Array methods](#array-methods). + + + +### Printing custom types + +If you want to define a custom print value for your type, simply define a +`str() string` method: + +```v +struct Color { + r int + g int + b int +} + +pub fn (c Color) str() string { + return '{${c.r}, ${c.g}, ${c.b}}' +} + +red := Color{ + r: 255 + g: 0 + b: 0 +} +println(red) +``` + +### Dumping expressions at runtime + +You can dump/trace the value of any V expression using `dump(expr)`. +For example, save this code sample as `factorial.v`, then run it with +`v run factorial.v`: + +```v +fn factorial(n u32) u32 { + if dump(n <= 1) { + return dump(1) + } + return dump(n * factorial(n - 1)) +} + +fn main() { + println(factorial(5)) +} +``` + +You will get: + +``` +[factorial.v:2] n <= 1: false +[factorial.v:2] n <= 1: false +[factorial.v:2] n <= 1: false +[factorial.v:2] n <= 1: false +[factorial.v:2] n <= 1: true +[factorial.v:3] 1: 1 +[factorial.v:5] n * factorial(n - 1): 2 +[factorial.v:5] n * factorial(n - 1): 6 +[factorial.v:5] n * factorial(n - 1): 24 +[factorial.v:5] n * factorial(n - 1): 120 +120 +``` + +Note that `dump(expr)` will trace both the source location, +the expression itself, and the expression value. diff --git a/aiprompts/ai_core/v_manual2.md b/aiprompts/ai_core/v_manual2.md new file mode 100644 index 00000000..e7581a35 --- /dev/null +++ b/aiprompts/ai_core/v_manual2.md @@ -0,0 +1,2009 @@ + +## Modules + +Every file in the root of a folder is part of the same module. +Simple programs don't need to specify module name, in which case it defaults to 'main'. + +See [symbol visibility](#symbol-visibility), [Access modifiers](#access-modifiers). + +### Create modules + +V is a very modular language. Creating reusable modules is encouraged and is +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 failcompile +// myfile.v +module mymodule + +// To export a function we have to use `pub` +pub fn say_hi() { + println('hello from mymodule!') +} +``` +All items inside a module can be used between the files of a module regardless of whether or +not they are prefaced with the `pub` keyword. +```v failcompile +// myfile2.v +module mymodule + +pub fn say_hi_and_bye() { + say_hi() // from myfile.v + println('goodbye from mymodule') +} +``` + +You can now use `mymodule` in your code: + +```v failcompile +import mymodule + +fn main() { + mymodule.say_hi() + mymodule.say_hi_and_bye() +} +``` + +* Module names should be short, under 10 characters. +* Module names must use `snake_case`. +* Circular imports are not allowed. +* You can have as many .v files in a module as you want. +* You can create modules anywhere. +* All modules are compiled statically into a single executable. + +### Special considerations for project folders + +For the top level project folder (the one, compiled with `v .`), and *only* +that folder, you can have several .v files, that may be mentioning different modules +with `module main`, `module abc` etc + +This is to ease the prototyping workflow in that folder: +- you can start developing some new project with a single .v file +- split functionality as necessary to different .v files in the same folder +- when that makes logical sense to be further organised, put them into their own directory module. + +Note that in ordinary modules, all .v files must start with `module name_of_folder`. + +### `init` functions + +If you want a module to automatically call some setup/initialization code when it is imported, +you can define a module `init` function: + +```v +fn init() { + // your setup code here ... +} +``` + +The `init` function cannot be public - it will be called automatically by V, *just once*, no matter +how many times the module was imported in your program. This feature is particularly useful for +initializing a C library. + +### `cleanup` functions + +If you want a module to automatically call some cleanup/deinitialization code, when your program +ends, you can define a module `cleanup` function: + +```v +fn cleanup() { + // your deinitialisation code here ... +} +``` + +Just like the `init` function, the `cleanup` function for a module cannot be public - it will be +called automatically, when your program ends, once per module, even if the module was imported +transitively by other modules several times, in the reverse order of the init calls. + +## Type Declarations + +### Type aliases + +To define a new type `NewType` as an alias for `ExistingType`, +do `type NewType = ExistingType`.
+This is a special case of a [sum type](#sum-types) declaration. + +### Enums + +```v +enum Color as u8 { + red + green + blue +} + +mut color := Color.red +// V knows that `color` is a `Color`. No need to use `color = Color.green` here. +color = .green +println(color) // "green" +match color { + .red { println('the color was red') } + .green { println('the color was green') } + .blue { println('the color was blue') } +} +``` + +The enum type can be any integer type, but can be omitted, if it is `int`: `enum Color {`. + +Enum match must be exhaustive or have an `else` branch. +This ensures that if a new enum field is added, it's handled everywhere in the code. + +Enum fields cannot re-use reserved keywords. However, reserved keywords may be escaped +with an @. + +```v +enum Color { + @none + red + green + blue +} + +color := Color.@none +println(color) +``` + +Integers may be assigned to enum fields. + +```v +enum Grocery { + apple + orange = 5 + pear +} + +g1 := int(Grocery.apple) +g2 := int(Grocery.orange) +g3 := int(Grocery.pear) +println('Grocery IDs: ${g1}, ${g2}, ${g3}') +``` + +Output: `Grocery IDs: 0, 5, 6`. + +Operations are not allowed on enum variables; they must be explicitly cast to `int`. + +Enums can have methods, just like structs. + +```v +enum Cycle { + one + two + three +} + +fn (c Cycle) next() Cycle { + match c { + .one { + return .two + } + .two { + return .three + } + .three { + return .one + } + } +} + +mut c := Cycle.one +for _ in 0 .. 10 { + println(c) + c = c.next() +} +``` + +Output: + +``` +one +two +three +one +two +three +one +two +three +one +``` + +Enums can be created from string or integer value and converted into string + +```v +enum Cycle { + one + two = 2 + three +} + +// Create enum from value +println(Cycle.from(10) or { Cycle.three }) +println(Cycle.from('two')!) + +// Convert an enum value to a string +println(Cycle.one.str()) +``` + +Output: + +``` +three +two +one +``` + +### Function Types + +You can use type aliases for naming specific function signatures - for +example: + +```v +type Filter = fn (string) string +``` + +This works like any other type - for example, a function can accept an +argument of a function type: + +```v +type Filter = fn (string) string + +fn filter(s string, f Filter) string { + return f(s) +} +``` + +V has duck-typing, so functions don't need to declare compatibility with +a function type - they just have to be compatible: + +```v +fn uppercase(s string) string { + return s.to_upper() +} + +// now `uppercase` can be used everywhere where Filter is expected +``` + +Compatible functions can also be explicitly cast to a function type: + +```v oksyntax +my_filter := Filter(uppercase) +``` + +The cast here is purely informational - again, duck-typing means that the +resulting type is the same without an explicit cast: + +```v oksyntax +my_filter := uppercase +``` + +You can pass the assigned function as an argument: + +```v oksyntax +println(filter('Hello world', my_filter)) // prints `HELLO WORLD` +``` + +And you could of course have passed it directly as well, without using a +local variable: + +```v oksyntax +println(filter('Hello world', uppercase)) +``` + +And this works with anonymous functions as well: + +```v oksyntax +println(filter('Hello world', fn (s string) string { + return s.to_upper() +})) +``` + +You can see the complete +[example here](https://github.com/vlang/v/tree/master/examples/function_types.v). + +### Interfaces + +```v +// interface-example.1 +struct Dog { + breed string +} + +fn (d Dog) speak() string { + return 'woof' +} + +struct Cat { + breed string +} + +fn (c Cat) speak() string { + return 'meow' +} + +// unlike Go, but like TypeScript, V's interfaces can define both fields and methods. +interface Speaker { + breed string + speak() string +} + +fn main() { + dog := Dog{'Leonberger'} + cat := Cat{'Siamese'} + + mut arr := []Speaker{} + arr << dog + arr << cat + for item in arr { + println('a ${item.breed} says: ${item.speak()}') + } +} +``` + +#### Implement an interface + +A type implements an interface by implementing its methods and fields. +There is no explicit declaration of intent, no "implements" keyword. + +An interface can have a `mut:` section. Implementing types will need +to have a `mut` receiver, for methods declared in the `mut:` section +of an interface. + +```v +// interface-example.2 +module main + +interface Foo { + write(string) string +} + +// => the method signature of a type, implementing interface Foo should be: +// `fn (s Type) write(a string) string` + +interface Bar { +mut: + write(string) string +} + +// => the method signature of a type, implementing interface Bar should be: +// `fn (mut s Type) write(a string) string` + +struct MyStruct {} + +// MyStruct implements the interface Foo, but *not* interface Bar +fn (s MyStruct) write(a string) string { + return a +} + +fn main() { + s1 := MyStruct{} + fn1(s1) + // fn2(s1) -> compile error, since MyStruct does not implement Bar +} + +fn fn1(s Foo) { + println(s.write('Foo')) +} + +// fn fn2(s Bar) { // does not match +// println(s.write('Foo')) +// } +``` + +#### Casting an interface + +We can test the underlying type of an interface using dynamic cast operators. +> [!NOTE] +> Dynamic cast converts variable `s` into a pointer inside the `if` statements in this example: + +```v oksyntax +// interface-example.3 (continued from interface-example.1) +interface Something {} + +fn announce(s Something) { + if s is Dog { + println('a ${s.breed} dog') // `s` is automatically cast to `Dog` (smart cast) + } else if s is Cat { + println('a cat speaks ${s.speak()}') + } else { + println('something else') + } +} + +fn main() { + dog := Dog{'Leonberger'} + cat := Cat{'Siamese'} + announce(dog) + announce(cat) +} +``` + +```v +// interface-example.4 +interface IFoo { + foo() +} + +interface IBar { + bar() +} + +// implements only IFoo +struct SFoo {} + +fn (sf SFoo) foo() {} + +// implements both IFoo and IBar +struct SFooBar {} + +fn (sfb SFooBar) foo() {} + +fn (sfb SFooBar) bar() { + dump('This implements IBar') +} + +fn main() { + mut arr := []IFoo{} + arr << SFoo{} + arr << SFooBar{} + + for a in arr { + dump(a) + // In order to execute instances that implements IBar. + if a is IBar { + a.bar() + } + } +} +``` + +For more information, see [Dynamic casts](#dynamic-casts). + +#### Interface method definitions + +Also unlike Go, an interface can have its own methods, similar to how +structs can have their methods. These 'interface methods' do not have +to be implemented, by structs which implement that interface. +They are just a convenient way to write `i.some_function()` instead of +`some_function(i)`, similar to how struct methods can be looked at, as +a convenience for writing `s.xyz()` instead of `xyz(s)`. + +> [!NOTE] +> This feature is NOT a "default implementation" like in C#. + +For example, if a struct `cat` is wrapped in an interface `a`, that has +implemented a method with the same name `speak`, as a method implemented by +the struct, and you do `a.speak()`, *only* the interface method is called: + +```v +interface Adoptable {} + +fn (a Adoptable) speak() string { + return 'adopt me!' +} + +struct Cat {} + +fn (c Cat) speak() string { + return 'meow!' +} + +struct Dog {} + +fn main() { + cat := Cat{} + assert dump(cat.speak()) == 'meow!' + + a := Adoptable(cat) + assert dump(a.speak()) == 'adopt me!' // call Adoptable's `speak` + if a is Cat { + // Inside this `if` however, V knows that `a` is not just any + // kind of Adoptable, but actually a Cat, so it will use the + // Cat `speak`, NOT the Adoptable `speak`: + dump(a.speak()) // meow! + } + + b := Adoptable(Dog{}) + assert dump(b.speak()) == 'adopt me!' // call Adoptable's `speak` + // if b is Dog { + // dump(b.speak()) // error: unknown method or field: Dog.speak + // } +} +``` + +#### Embedded interface + +Interfaces support embedding, just like structs: + +```v +pub interface Reader { +mut: + read(mut buf []u8) ?int +} + +pub interface Writer { +mut: + write(buf []u8) ?int +} + +// ReaderWriter embeds both Reader and Writer. +// The effect is the same as copy/pasting all of the +// Reader and all of the Writer methods/fields into +// ReaderWriter. +pub interface ReaderWriter { + Reader + Writer +} +``` + +### Sum types + +A sum type instance can hold a value of several different types. Use the `type` +keyword to declare a sum type: + +```v +struct Moon {} + +struct Mars {} + +struct Venus {} + +type World = Mars | Moon | Venus + +sum := World(Moon{}) +assert sum.type_name() == 'Moon' +println(sum) +``` + +The built-in method `type_name` returns the name of the currently held +type. + +With sum types you could build recursive structures and write concise but powerful code on them. + +```v +// V's binary tree +struct Empty {} + +struct Node { + value f64 + left Tree + right Tree +} + +type Tree = Empty | Node + +// sum up all node values + +fn sum(tree Tree) f64 { + return match tree { + Empty { 0 } + Node { tree.value + sum(tree.left) + sum(tree.right) } + } +} + +fn main() { + left := Node{0.2, Empty{}, Empty{}} + right := Node{0.3, Empty{}, Node{0.4, Empty{}, Empty{}}} + tree := Node{0.5, left, right} + println(sum(tree)) // 0.2 + 0.3 + 0.4 + 0.5 = 1.4 +} +``` + +#### Dynamic casts + +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 +struct Moon {} + +struct Mars {} + +struct Venus {} + +type World = Mars | Moon | Venus + +fn (m Mars) dust_storm() bool { + return true +} + +fn main() { + mut w := World(Moon{}) + assert w is Moon + w = Mars{} + // use `as` to access the Mars instance + mars := w as Mars + if mars.dust_storm() { + println('bad weather!') + } +} +``` + +`as` will panic if `w` doesn't hold a `Mars` instance. +A safer way is to use a smart cast. + +#### Smart casting + +```v oksyntax +if w is Mars { + assert typeof(w).name == 'Mars' + if w.dust_storm() { + println('bad weather!') + } +} +``` + +`w` has type `Mars` inside the body of the `if` statement. This is +known as *flow-sensitive typing*. +If `w` is a mutable identifier, it would be unsafe if the compiler smart casts it without a warning. +That's why you have to declare a `mut` before the `is` expression: + +```v ignore +if mut w is Mars { + assert typeof(w).name == 'Mars' + if w.dust_storm() { + println('bad weather!') + } +} +``` + +Otherwise `w` would keep its original type. +> This works for both simple variables and complex expressions like `user.name` + +#### Matching sum types + +You can also use `match` to determine the variant: + +```v +struct Moon {} + +struct Mars {} + +struct Venus {} + +type World = Mars | Moon | Venus + +fn open_parachutes(n int) { + println(n) +} + +fn land(w World) { + match w { + Moon {} // no atmosphere + Mars { + // light atmosphere + open_parachutes(3) + } + Venus { + // heavy atmosphere + open_parachutes(1) + } + } +} +``` + +`match` must have a pattern for each variant or have an `else` branch. + +```v ignore +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 { + // using the shadowed match variable, in this case `w` (smart cast) + Moon { w.moon_walk() } + Mars { w.shiver() } + else {} + } +} +``` + +### Option/Result types and error handling + +Option types are for types which may represent `none`. Result types may +represent an error returned from a function. + +`Option` types are declared by prepending `?` to the type name: `?Type`. +`Result` types use `!`: `!Type`. + +```v +struct User { + id int + name string +} + +struct Repo { + users []User +} + +fn (r Repo) find_user_by_id(id int) !User { + for user in r.users { + if user.id == id { + // V automatically wraps this into a result or option type + return user + } + } + return error('User ${id} not found') +} + +// A version of the function using an option +fn (r Repo) find_user_by_id2(id int) ?User { + for user in r.users { + if user.id == id { + return user + } + } + return none +} + +fn main() { + repo := Repo{ + users: [User{1, 'Andrew'}, User{2, 'Bob'}, User{10, 'Charles'}] + } + user := repo.find_user_by_id(10) or { // Option/Result types must be handled by `or` blocks + println(err) + return + } + println(user.id) // "10" + println(user.name) // "Charles" + + user2 := repo.find_user_by_id2(10) or { return } + + // To create an Option var directly: + my_optional_int := ?int(none) + my_optional_string := ?string(none) + my_optional_user := ?User(none) +} +``` + +V used to combine `Option` and `Result` into one type, now they are separate. + +The amount of work required to "upgrade" a function to an option/result function is minimal; +you have to add a `?` or `!` to the return type and return `none` or an error (respectively) +when something goes wrong. + +This is the primary mechanism for error handling in V. They are still values, like in Go, +but the advantage is that errors can't be unhandled, and handling them is a lot less verbose. +Unlike other languages, V does not handle exceptions with `throw/try/catch` blocks. + +`err` is defined inside an `or` block and is set to the string message passed +to the `error()` function. + +```v oksyntax +user := repo.find_user_by_id(7) or { + println(err) // "User 7 not found" + return +} +``` + +#### Options/results when returning multiple values + +Only one `Option` or `Result` is allowed to be returned from a function. It is +possible to return multiple values and still signal an error. + +```v +fn multireturn(v int) !(int, int) { + if v < 0 { + return error('must be positive') + } + return v, v * v +} +``` + +#### Handling options/results + +There are four ways of handling an option/result. The first method is to +propagate the error: + +```v +import net.http + +fn f(url string) !string { + resp := http.get(url)! + return resp.body +} +``` + +`http.get` returns `!http.Response`. Because `!` follows the call, the +error will be propagated to the caller of `f`. When using `?` after a +function call producing an option, the enclosing function must return +an option as well. If error propagation is used in the `main()` +function it will `panic` instead, since the error cannot be propagated +any further. + +The body of `f` is essentially a condensed version of: + +```v ignore + resp := http.get(url) or { return err } + return resp.body +``` + +--- +The second method is to break from execution early: + +```v oksyntax +user := repo.find_user_by_id(7) or { return } +``` + +Here, you can either call `panic()` or `exit()`, which will stop the execution of the +entire program, or use a control flow statement (`return`, `break`, `continue`, etc) +to break from the current block. + +> [!NOTE] +> `break` and `continue` can only be used inside a `for` loop. + +V does not have a way to forcibly "unwrap" an option (as other languages do, +for instance Rust's `unwrap()` or Swift's `!`). To do this, use `or { panic(err) }` instead. + +--- +The third method is to provide a default value at the end of the `or` block. +In case of an error, that value would be assigned instead, +so it must have the same type as the content of the `Option` being handled. + +```v +fn do_something(s string) !string { + if s == 'foo' { + return 'foo' + } + return error('invalid 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 +import net.http + +if resp := http.get('https://google.com') { + println(resp.body) // resp is a http.Response, not an option +} else { + println(err) +} +``` + +Above, `http.get` returns a `!http.Response`. `resp` is only in scope for the first +`if` branch. `err` is only in scope for the `else` branch. + +### Custom error types + +V gives you the ability to define custom error types through the `IError` interface. +The interface requires two methods: `msg() string` and `code() int`. Every type that +implements these methods can be used as an error. + +When defining a custom error type it is recommended to embed the builtin `Error` default +implementation. This provides an empty default implementation for both required methods, +so you only have to implement what you really need, and may provide additional utility +functions in the future. + +```v +struct PathError { + Error + path string +} + +fn (err PathError) msg() string { + return 'Failed to open path: ${err.path}' +} + +fn try_open(path string) ! { + // V automatically casts this to IError + return PathError{ + path: path + } +} + +fn main() { + try_open('/tmp') or { panic(err) } +} +``` + +### Generics + +```v wip + +struct Repo[T] { + db DB +} + +struct User { + id int + name string +} + +struct Post { + id int + user_id int + title string + body string +} + +fn new_repo[T](db DB) Repo[T] { + return Repo[T]{db: db} +} + +// This is a generic function. V will generate it for every type it's used with. +fn (r Repo[T]) find_by_id(id int) ?T { + table_name := T.name // in this example getting the name of the type gives us the table name + return r.db.query_one[T]('select * from ${table_name} where id = ?', id) +} + +db := new_db() +users_repo := new_repo[User](db) // returns Repo[User] +posts_repo := new_repo[Post](db) // returns Repo[Post] +user := users_repo.find_by_id(1)? // find_by_id[User] +post := posts_repo.find_by_id(1)? // find_by_id[Post] +``` + +Currently generic function definitions must declare their type parameters, but in +future V will infer generic type parameters from single-letter type names in +runtime parameter types. This is why `find_by_id` can omit `[T]`, because the +receiver argument `r` uses a generic type `T`. + +Another example: + +```v +fn compare[T](a T, b T) int { + if a < b { + return -1 + } + if a > b { + return 1 + } + return 0 +} + +// compare[int] +println(compare(1, 0)) // Outputs: 1 +println(compare(1, 1)) // 0 +println(compare(1, 2)) // -1 +// compare[string] +println(compare('1', '0')) // Outputs: 1 +println(compare('1', '1')) // 0 +println(compare('1', '2')) // -1 +// compare[f64] +println(compare(1.1, 1.0)) // Outputs: 1 +println(compare(1.1, 1.1)) // 0 +println(compare(1.1, 1.2)) // -1 +``` + +## Concurrency + +### Spawning Concurrent Tasks + +V's model of concurrency is going to be very similar to Go's. +For now, `spawn foo()` runs `foo()` concurrently in a different thread: + +```v +import math + +fn p(a f64, b f64) { // ordinary function without return value + c := math.sqrt(a * a + b * b) + println(c) +} + +fn main() { + spawn p(3, 4) + // p will be run in parallel thread + // It can also be written as follows + // spawn fn (a f64, b f64) { + // c := math.sqrt(a * a + b * b) + // println(c) + // }(3, 4) +} +``` + +> [!NOTE] +> Threads rely on the machine's CPU (number of cores/threads). +> Be aware that OS threads spawned with `spawn` +> have limitations in regard to concurrency, +> including resource overhead and scalability issues, +> and might affect performance in cases of high thread count. + +There's also a `go` keyword. Right now `go foo()` will be automatically renamed via vfmt +to `spawn foo()`, and there will be a way to launch a coroutine with `go` (a lightweight +thread managed by the runtime). + +Sometimes it is necessary to wait until a parallel thread has finished. This can +be done by assigning a *handle* to the started thread and calling the `wait()` method +to this handle later: + +```v +import math + +fn p(a f64, b f64) { // ordinary function without return value + c := math.sqrt(a * a + b * b) + println(c) // prints `5` +} + +fn main() { + h := spawn p(3, 4) + // p() runs in parallel thread + h.wait() + // p() has definitely finished +} +``` + +This approach can also be used to get a return value from a function that is run in a +parallel thread. There is no need to modify the function itself to be able to call it +concurrently. + +```v +import math { sqrt } + +fn get_hypot(a f64, b f64) f64 { // ordinary function returning a value + c := sqrt(a * a + b * b) + return c +} + +fn main() { + g := spawn get_hypot(54.06, 2.08) // spawn thread and get handle to it + h1 := get_hypot(2.32, 16.74) // do some other calculation here + h2 := g.wait() // get result from spawned thread + println('Results: ${h1}, ${h2}') // prints `Results: 16.9, 54.1` +} +``` + +If there is a large number of tasks, it might be easier to manage them +using an array of threads. + +```v +import time + +fn task(id int, duration int) { + println('task ${id} begin') + time.sleep(duration * time.millisecond) + println('task ${id} end') +} + +fn main() { + mut threads := []thread{} + threads << spawn task(1, 500) + threads << spawn task(2, 900) + threads << spawn task(3, 100) + threads.wait() + println('done') +} + +// Output: +// task 1 begin +// task 2 begin +// task 3 begin +// task 3 end +// task 1 end +// task 2 end +// done +``` + +Additionally for threads that return the same type, calling `wait()` +on the thread array will return all computed values. + +```v +fn expensive_computing(i int) int { + return i * i +} + +fn main() { + mut threads := []thread int{} + for i in 1 .. 10 { + threads << spawn expensive_computing(i) + } + // Join all tasks + r := threads.wait() + println('All jobs finished: ${r}') +} + +// Output: All jobs finished: [1, 4, 9, 16, 25, 36, 49, 64, 81] +``` + +### Channels + +Channels are the preferred way to communicate between threads. V's channels work basically like +those in Go. You can push objects into a channel on one end and pop objects from the other end. +Channels can be buffered or unbuffered and it is possible to `select` from multiple channels. + +#### Syntax and Usage + +Channels have the type `chan objtype`. An optional buffer length can be specified as the `cap` field +in the declaration: + +```v +ch := chan int{} // unbuffered - "synchronous" +ch2 := chan f64{cap: 100} // buffer length 100 +``` + +Channels do not have to be declared as `mut`. The buffer length is not part of the type but +a field of the individual channel object. Channels can be passed to threads like normal +variables: + +```v +fn f(ch chan int) { + // ... +} + +fn main() { + ch := chan int{} + spawn f(ch) + // ... +} +``` + +Objects can be pushed to channels using the arrow operator. The same operator can be used to +pop objects from the other end: + +```v +// make buffered channels so pushing does not block (if there is room in the buffer) +ch := chan int{cap: 1} +ch2 := chan f64{cap: 1} +n := 5 +// push +ch <- n +ch2 <- 7.3 +mut y := f64(0.0) +m := <-ch // pop creating new variable +y = <-ch2 // pop into existing variable +``` + +A channel can be closed to indicate that no further objects can be pushed. Any attempt +to do so will then result in a runtime panic (with the exception of `select` and +`try_push()` - see below). Attempts to pop will return immediately if the +associated channel has been closed and the buffer is empty. This situation can be +handled using an `or {}` block (see [Handling options/results](#handling-optionsresults)). + +```v wip +ch := chan int{} +ch2 := chan f64{} +// ... +ch.close() +// ... +m := <-ch or { + println('channel has been closed') +} + +// propagate error +y := <-ch2 ? +``` + +#### Channel Select + +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 +import time + +fn main() { + ch := chan f64{} + ch2 := chan f64{} + ch3 := chan f64{} + mut b := 0.0 + c := 1.0 + // ... setup spawn threads that will send on ch/ch2 + spawn fn (the_channel chan f64) { + time.sleep(5 * time.millisecond) + the_channel <- 1.0 + }(ch) + spawn fn (the_channel chan f64) { + time.sleep(1 * time.millisecond) + the_channel <- 1.0 + }(ch2) + spawn fn (the_channel chan f64) { + _ := <-the_channel + }(ch3) + + select { + a := <-ch { + // do something with `a` + eprintln('> a: ${a}') + } + b = <-ch2 { + // do something with predeclared variable `b` + eprintln('> b: ${b}') + } + ch3 <- c { + // do something if `c` was sent + time.sleep(5 * time.millisecond) + eprintln('> c: ${c} was send on channel ch3') + } + 500 * time.millisecond { + // do something if no channel has become ready within 0.5s + eprintln('> more than 0.5s passed without a channel being ready') + } + } + eprintln('> done') +} +``` + +The timeout branch is optional. If it is absent `select` waits for an unlimited amount of time. +It is also possible to proceed immediately if no channel is ready in the moment `select` is called +by adding an `else { ... }` branch. `else` and `` are mutually exclusive. + +The `select` command can be used as an *expression* of type `bool` +that becomes `false` if all channels are closed: + +```v wip +if select { + ch <- a { + // ... + } +} { + // channel was open +} else { + // channel is closed +} +``` + +#### Special Channel Features + +For special purposes there are some builtin fields and methods: + +```v +struct Abc { + x int +} + +a := 2.13 +ch := chan f64{} +res := ch.try_push(a) // try to perform `ch <- a` +println(res) +l := ch.len // number of elements in queue +c := ch.cap // maximum queue length +is_closed := ch.closed // bool flag - has `ch` been closed +println(l) +println(c) +mut b := Abc{} +ch2 := chan Abc{} +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 +the reason why not. +Usage of these methods and fields in production is not recommended - +algorithms based on them are often subject to race conditions. Especially `.len` and +`.closed` should not be used to make decisions. +Use `or` branches, error propagation or `select` instead (see [Syntax and Usage](#syntax-and-usage) +and [Channel Select](#channel-select) above). + +### Shared Objects + +Data can be exchanged between a thread and the calling thread via a shared variable. +Such variables should be created as `shared` and passed to the thread as such, too. +The underlying `struct` contains a hidden *mutex* that allows locking concurrent access +using `rlock` for read-only and `lock` for read/write access. + +```v +struct St { +mut: + x int // data to be shared +} + +fn (shared b St) g() { + lock b { + // read/modify/write b.x + } +} + +fn main() { + shared a := St{ + x: 10 + } + spawn a.g() + // ... + rlock a { + // read a.x + } +} +``` + +Shared variables must be structs, arrays or maps. + +## JSON + +Because of the ubiquitous nature of JSON, support for it is built directly into V. + +V generates code for JSON encoding and decoding. +No runtime reflection is used. This results in much better performance. + +### Decoding JSON + +```v +import json + +struct Foo { + x int +} + +struct User { + // Adding a [required] attribute will make decoding fail, if that + // field is not present in the input. + // If a field is not [required], but is missing, it will be assumed + // to have its default value, like 0 for numbers, or '' for strings, + // and decoding will not fail. + name string @[required] + age int + // Use the `skip` attribute to skip certain fields + foo Foo @[skip] + // If the field name is different in JSON, it can be specified + last_name string @[json: lastName] +} + +data := '{ "name": "Frodo", "lastName": "Baggins", "age": 25 }' +user := json.decode(User, data) or { + eprintln('Failed to decode json, error: ${err}') + return +} +println(user.name) +println(user.last_name) +println(user.age) +// You can also decode JSON arrays: +sfoos := '[{"x":123},{"x":456}]' +foos := json.decode([]Foo, sfoos)! +println(foos[0].x) +println(foos[1].x) +``` + +The `json.decode` function takes two arguments: +the first is the type into which the JSON value should be decoded and +the second is a string containing the JSON data. + +### Encoding JSON + +```v +import json + +struct User { + name string + score i64 +} + +mut data := map[string]int{} +user := &User{ + name: 'Pierre' + score: 1024 +} + +data['x'] = 42 +data['y'] = 360 + +println(json.encode(data)) // {"x":42,"y":360} +println(json.encode(user)) // {"name":"Pierre","score":1024} +``` + +The json module also supports anonymous struct fields, which helps with complex JSON apis with lots +of levels. + +## Testing + +### Asserts + +```v +fn foo(mut v []int) { + v[0] = 1 +} + +mut v := [20] +foo(mut v) +assert v[0] < 4 +``` + +An `assert` statement checks that its expression evaluates to `true`. If an assert fails, +the program will usually abort. Asserts should only be used to detect programming errors. When an +assert fails it is reported to *stderr*, and the values on each side of a comparison operator +(such as `<`, `==`) will be printed when possible. This is useful to easily find an +unexpected value. Assert statements can be used in any function, not just test ones, +which is handy when developing new functionality, to keep your invariants in check. + +> [!NOTE] +> All `assert` statements are *removed*, when you compile your program with the `-prod` flag. + +### Asserts with an extra message + +This form of the `assert` statement, will print the extra message when it fails. Note that +you can use any string expression there - string literals, functions returning a string, +strings that interpolate variables, etc. + +```v +fn test_assertion_with_extra_message_failure() { + for i in 0 .. 100 { + assert i * 2 - 45 < 75 + 10, 'assertion failed for i: ${i}' + } +} +``` + +### Asserts that do not abort your program + +When initially prototyping functionality and tests, it is sometimes desirable to +have asserts that do not stop the program, but just print their failures. That can +be achieved by tagging your assert containing functions with an `[assert_continues]` +tag, for example running this program: + +```v +@[assert_continues] +fn abc(ii int) { + assert ii == 2 +} + +for i in 0 .. 4 { + abc(i) +} +``` + +... will produce this output: + +``` +assert_continues_example.v:3: FAIL: fn main.abc: assert ii == 2 + left value: ii = 0 + right value: 2 +assert_continues_example.v:3: FAIL: fn main.abc: assert ii == 2 + left value: ii = 1 + right value: 2 +assert_continues_example.v:3: FAIL: fn main.abc: assert ii == 2 + left value: ii = 3 + right value: 2 +``` + +> [!NOTE] +> V also supports a command line flag `-assert continues`, which will change the +> behaviour of all asserts globally, as if you had tagged every function with `[assert_continues]`. + +### Test files + +```v +// hello.v +module main + +fn hello() string { + return 'Hello world' +} + +fn main() { + println(hello()) +} +``` + +```v failcompile +// hello_test.v +module main + +fn test_hello() { + assert hello() == 'Hello world' +} +``` + +To run the test file above, use `v hello_test.v`. This will check that the function `hello` is +producing the correct output. V executes all test functions in the file. + +> [!NOTE] +> All `_test.v` files (both external and internal ones), are compiled as *separate programs*. +> In other words, you may have as many `_test.v` files, and tests in them as you like, they will +> not affect the compilation of your other code in `.v` files normally at all, but only when you +> do explicitly `v file_test.v` or `v test .`. + +* All test functions have to be inside a test file whose name ends in `_test.v`. +* Test function names must begin with `test_` to mark them for execution. +* Normal functions can also be defined in test files, and should be called manually. Other + symbols can also be defined in test files e.g. types. +* There are two kinds of tests: external and internal. +* Internal tests must *declare* their module, just like all other .v + files from the same module. Internal tests can even call private functions in + the same module. +* External tests must *import* the modules which they test. They do not + have access to the private functions/types of the modules. They can test only + the external/public API that a module provides. + +In the example above, `test_hello` is an internal test that can call +the private function `hello()` because `hello_test.v` has `module main`, +just like `hello.v`, i.e. both are part of the same module. Note also that +since `module main` is a regular module like the others, internal tests can +be used to test private functions in your main program .v files too. + +You can also define these special test functions in a test file: + +* `testsuite_begin` which will be run *before* all other test functions. +* `testsuite_end` which will be run *after* all other test functions. + +If a test function has an error return type, any propagated errors will fail the test: + +```v +import strconv + +fn test_atoi() ! { + assert strconv.atoi('1')! == 1 + assert strconv.atoi('one')! == 1 // test will fail +} +``` + +### Running tests + +To run test functions in an individual test file, use `v foo_test.v`. + +To test an entire module, use `v test mymodule`. You can also use `v test .` to test +everything inside your current folder (and subfolders). You can pass the `-stats` +option to see more details about the individual tests run. + +You can put additional test data, including .v source files in a folder, named +`testdata`, right next to your _test.v files. V's test framework will *ignore* +such folders, while scanning for tests to run. This is useful, if you want to +put .v files with invalid V source code, or other tests, including known +failing ones, that should be run in a specific way/options by a parent _test.v +file. + +> [!NOTE] +> The path to the V compiler, is available through @VEXE, so a _test.v +> file, can easily run *other* test files like this: + +```v oksyntax +import os + +fn test_subtest() { + res := os.execute('${os.quoted_path(@VEXE)} other_test.v') + assert res.exit_code == 1 + assert res.output.contains('other_test.v does not exist') +} +``` + +## Memory management + +V avoids doing unnecessary allocations in the first place by using value types, +string buffers, promoting a simple abstraction-free code style. + +There are 4 ways to manage memory in V. + +The default is a minimal and a well performing tracing GC. + +The second way is autofree, it can be enabled with `-autofree`. It takes care of most objects +(~90-100%): the compiler inserts necessary free calls automatically during compilation. +Remaining small percentage of objects is freed via GC. The developer doesn't need to change +anything in their code. "It just works", like in Python, Go, or Java, except there's no +heavy GC tracing everything or expensive RC for each object. + +For developers willing to have more low level control, memory can be managed manually with +`-gc none`. + +Arena allocation is available via v `-prealloc`. + +### Control + +You can take advantage of V's autofree engine and define a `free()` method on custom +data types: + +```v +struct MyType {} + +@[unsafe] +fn (data &MyType) free() { + // ... +} +``` + +Just as the compiler frees C data types with C's `free()`, it will statically insert +`free()` calls for your data type at the end of each variable's lifetime. + +Autofree can be enabled with an `-autofree` flag. + +For developers willing to have more low level control, autofree can be disabled with +`-manualfree`, or by adding a `[manualfree]` on each function that wants to manage its +memory manually. (See [attributes](#attributes)). + +> [!NOTE] +> Autofree is still WIP. Until it stabilises and becomes the default, please +> avoid using it. Right now allocations are handled by a minimal and well performing GC +> until V's autofree engine is production ready. + +**Examples** + +```v +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) + // ... +} +``` + +The strings don't escape `draw_text`, so they are cleaned up when +the function exits. + +In fact, with the `-prealloc` flag, the first two calls won't result in any allocations at all. +These two strings are small, so 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 + numbers := [1, 2, 3] // array allocated on heap, will be freed as the function exits + println(number) + println(user) + println(numbers) + numbers2 := [4, 5, 6] // array that's being returned, won't be freed here + return numbers2 +} +``` + +### Stack and Heap + +#### Stack and Heap Basics + +Like with most other programming languages there are two locations where data can +be stored: + +* The *stack* allows fast allocations with almost zero administrative overhead. The + stack grows and shrinks with the function call depth – so every called + function has its stack segment that remains valid until the function returns. + No freeing is necessary, however, this also means that a reference to a stack + object becomes invalid on function return. Furthermore stack space is + limited (typically to a few Megabytes per thread). +* The *heap* is a large memory area (typically some Gigabytes) that is administrated + by the operating system. Heap objects are allocated and freed by special function + calls that delegate the administrative tasks to the OS. This means that they can + remain valid across several function calls, however, the administration is + expensive. + +#### V's default approach + +Due to performance considerations V tries to put objects on the stack if possible +but allocates them on the heap when obviously necessary. Example: + +```v +struct MyStruct { + n int +} + +struct RefStruct { + r &MyStruct +} + +fn main() { + q, w := f() + println('q: ${q.r.n}, w: ${w.n}') +} + +fn f() (RefStruct, &MyStruct) { + a := MyStruct{ + n: 1 + } + b := MyStruct{ + n: 2 + } + c := MyStruct{ + n: 3 + } + e := RefStruct{ + r: &b + } + x := a.n + c.n + println('x: ${x}') + return e, &c +} +``` + +Here `a` is stored on the stack since its address never leaves the function `f()`. +However a reference to `b` is part of `e` which is returned. Also a reference to +`c` is returned. For this reason `b` and `c` will be heap allocated. + +Things become less obvious when a reference to an object is passed as a function argument: + +```v +struct MyStruct { +mut: + n int +} + +fn main() { + mut q := MyStruct{ + n: 7 + } + w := MyStruct{ + n: 13 + } + x := q.f(&w) // references of `q` and `w` are passed + println('q: ${q}\nx: ${x}') +} + +fn (mut a MyStruct) f(b &MyStruct) int { + a.n += b.n + x := a.n * b.n + return x +} +``` + +Here the call `q.f(&w)` passes references to `q` and `w` because `a` is +`mut` and `b` is of type `&MyStruct` in `f()`'s declaration, so technically +these references are leaving `main()`. However the *lifetime* of these +references lies inside the scope of `main()` so `q` and `w` are allocated +on the stack. + +#### Manual Control for Stack and Heap + +In the last example the V compiler could put `q` and `w` on the stack +because it assumed that in the call `q.f(&w)` these references were only +used for reading and modifying the referred values – and not to pass the +references themselves somewhere else. This can be seen in a way that the +references to `q` and `w` are only *borrowed* to `f()`. + +Things become different if `f()` is doing something with a reference itself: + +```v +struct RefStruct { +mut: + r &MyStruct +} + +// see discussion below +@[heap] +struct MyStruct { + n int +} + +fn main() { + mut m := MyStruct{} + mut r := RefStruct{ + r: &m + } + r.g() + println('r: ${r}') +} + +fn (mut r RefStruct) g() { + s := MyStruct{ + n: 7 + } + r.f(&s) // reference to `s` inside `r` is passed back to `main() ` +} + +fn (mut r RefStruct) f(s &MyStruct) { + r.r = s // would trigger error without `[heap]` +} +``` + +Here `f()` looks quite innocent but is doing nasty things – it inserts a +reference to `s` into `r`. The problem with this is that `s` lives only as long +as `g()` is running but `r` is used in `main()` after that. For this reason +the compiler would complain about the assignment in `f()` because `s` *"might +refer to an object stored on stack"*. The assumption made in `g()` that the call +`r.f(&s)` would only borrow the reference to `s` is wrong. + +A solution to this dilemma is the `[heap]` [attribute](#attributes) at the declaration of +`struct MyStruct`. It instructs the compiler to *always* allocate `MyStruct`-objects +on the heap. This way the reference to `s` remains valid even after `g()` returns. +The compiler takes into consideration that `MyStruct` objects are always heap +allocated when checking `f()` and allows assigning the reference to `s` to the +`r.r` field. + +There is a pattern often seen in other programming languages: + +```v failcompile +fn (mut a MyStruct) f() &MyStruct { + // do something with a + return &a // would return address of borrowed object +} +``` + +Here `f()` is passed a reference `a` as receiver that is passed back to the caller and returned +as result at the same time. The intention behind such a declaration is method chaining like +`y = x.f().g()`. However, the problem with this approach is that a second reference +to `a` is created – so it is not only borrowed and `MyStruct` has to be +declared as `[heap]`. + +In V the better approach is: + +```v +struct MyStruct { +mut: + n int +} + +fn (mut a MyStruct) f() { + // do something with `a` +} + +fn (mut a MyStruct) g() { + // do something else with `a` +} + +fn main() { + x := MyStruct{} // stack allocated + mut y := x + y.f() + y.g() + // instead of `mut y := x.f().g() +} +``` + +This way the `[heap]` attribute can be avoided – resulting in better performance. + +However, stack space is very limited as mentioned above. For this reason the `[heap]` +attribute might be suitable for very large structures even if not required by use cases +like those mentioned above. + +There is an alternative way to manually control allocation on a case to case basis. This +approach is not recommended but shown here for the sake of completeness: + +```v +struct MyStruct { + n int +} + +struct RefStruct { +mut: + r &MyStruct +} + +// simple function - just to overwrite stack segment previously used by `g()` + +fn use_stack() { + x := 7.5 + y := 3.25 + z := x + y + println('${x} ${y} ${z}') +} + +fn main() { + mut m := MyStruct{} + mut r := RefStruct{ + r: &m + } + r.g() + use_stack() // to erase invalid stack contents + println('r: ${r}') +} + +fn (mut r RefStruct) g() { + s := &MyStruct{ // `s` explicitly refers to a heap object + n: 7 + } + // change `&MyStruct` -> `MyStruct` above and `r.f(s)` -> `r.f(&s)` below + // to see data in stack segment being overwritten + r.f(s) +} + +fn (mut r RefStruct) f(s &MyStruct) { + r.r = unsafe { s } // override compiler check +} +``` + +Here the compiler check is suppressed by the `unsafe` block. To make `s` be heap +allocated even without `[heap]` attribute the `struct` literal is prefixed with +an ampersand: `&MyStruct{...}`. + +This last step would not be required by the compiler but without it the reference +inside `r` becomes invalid (the memory area pointed to will be overwritten by +`use_stack()`) and the program might crash (or at least produce an unpredictable +final output). That's why this approach is *unsafe* and should be avoided! + +## ORM + +(This is still in an alpha state) + +V has a built-in ORM (object-relational mapping) which supports SQLite, MySQL and Postgres, +but soon it will support MS SQL and Oracle. + +V's ORM provides a number of benefits: + +- One syntax for all SQL dialects. (Migrating between databases becomes much easier.) +- Queries are constructed using V's syntax. (There's no need to learn another syntax.) +- Safety. (All queries are automatically sanitised to prevent SQL injection.) +- Compile time checks. (This prevents typos which can only be caught during runtime.) +- Readability and simplicity. (You don't need to manually parse the results of a query and + then manually construct objects from the parsed results.) + +```v +import db.sqlite + +// sets a custom table name. Default is struct name (case-sensitive) +@[table: 'customers'] +struct Customer { + id int @[primary; sql: serial] // a field named `id` of integer type must be the first field + name string + nr_orders int + country ?string +} + +db := sqlite.connect('customers.db')! + +// You can create tables from your struct declarations. For example the next query will issue SQL similar to this: +// CREATE TABLE IF NOT EXISTS `Customer` ( +// `id` INTEGER PRIMARY KEY, +// `name` TEXT NOT NULL, +// `nr_orders` INTEGER NOT NULL, +// `country` TEXT +// ) +sql db { + create table Customer +}! + +// insert a new customer: +new_customer := Customer{ + name: 'Bob' + country: 'uk' + nr_orders: 10 +} +sql db { + insert new_customer into Customer +}! + +us_customer := Customer{ + name: 'Martin' + country: 'us' + nr_orders: 5 +} +sql db { + insert us_customer into Customer +}! + +none_country_customer := Customer{ + name: 'Dennis' + country: none + nr_orders: 2 +} +sql db { + insert none_country_customer into Customer +}! + +// update a customer: +sql db { + update Customer set nr_orders = nr_orders + 1 where name == 'Bob' +}! + +// select count(*) from customers +nr_customers := sql db { + select count from Customer +}! +println('number of all customers: ${nr_customers}') + +// V's syntax can be used to build queries: +uk_customers := sql db { + select from Customer where country == 'uk' && nr_orders > 0 +}! +println('We found a total of ${uk_customers.len} customers matching the query.') +for c in uk_customers { + println('customer: ${c.id}, ${c.name}, ${c.country}, ${c.nr_orders}') +} + +none_country_customers := sql db { + select from Customer where country is none +}! +println('We found a total of ${none_country_customers.len} customers, with no country set.') +for c in none_country_customers { + println('customer: ${c.id}, ${c.name}, ${c.country}, ${c.nr_orders}') +} + +// delete a customer +sql db { + delete from Customer where name == 'Bob' +}! +``` + +For more examples and the docs, see [vlib/orm](https://github.com/vlang/v/tree/master/vlib/orm). diff --git a/aiprompts/binencoder.md b/aiprompts/binencoder.md new file mode 120000 index 00000000..cf88682e --- /dev/null +++ b/aiprompts/binencoder.md @@ -0,0 +1 @@ +../lib/data/encoder/readme.md \ No newline at end of file diff --git a/aiprompts/currency.md b/aiprompts/currency.md new file mode 120000 index 00000000..ed82901f --- /dev/null +++ b/aiprompts/currency.md @@ -0,0 +1 @@ +../lib/data/currency/readme.md \ No newline at end of file diff --git a/aiprompts/datatypes.md b/aiprompts/datatypes.md new file mode 100644 index 00000000..a2c4f298 --- /dev/null +++ b/aiprompts/datatypes.md @@ -0,0 +1,340 @@ +module datatypes + +# datatypes + +This module provides implementations of less frequently used, but still common data types. + +V's `builtin` module is imported implicitly, and has implementations for arrays, maps and strings. These are good for many applications, but there are a plethora of other useful data structures/containers, like linked lists, priority queues, trees, etc, that allow for algorithms with different time complexities, which may be more suitable for your specific application. + +It is implemented using generics, that you have to specialise for the type of your actual elements. For example: +```v +import datatypes + +mut stack := datatypes.Stack[int]{} +stack.push(1) +println(stack) +``` + +## Currently Implemented Datatypes: + +- [x] Linked list +- [x] Doubly linked list +- [x] Stack (LIFO) +- [x] Queue (FIFO) +- [x] Min heap (priority queue) +- [x] Set +- [x] Quadtree +- [x] Bloom filter +- [ ] ... + + +fn new_bloom_filter[T](hash_func fn (T) u32, table_size int, num_functions int) !&BloomFilter[T] + new_bloom_filter creates a new bloom_filter. `table_size` should be greater than 0, and `num_functions` should be 1~16. +fn new_bloom_filter_fast[T](hash_func fn (T) u32) &BloomFilter[T] + new_bloom_filter_fast creates a new bloom_filter. `table_size` is 16384, and `num_functions` is 4. +fn new_ringbuffer[T](s int) RingBuffer[T] + new_ringbuffer creates an empty ring buffer of size `s`. +fn (mut bst BSTree[T]) insert(value T) bool + insert give the possibility to insert an element in the BST. +fn (bst &BSTree[T]) contains(value T) bool + contains checks if an element with a given `value` is inside the BST. +fn (mut bst BSTree[T]) remove(value T) bool + remove removes an element with `value` from the BST. +fn (bst &BSTree[T]) is_empty() bool + is_empty checks if the BST is empty +fn (bst &BSTree[T]) in_order_traversal() []T + in_order_traversal traverses the BST in order, and returns the result as an array. +fn (bst &BSTree[T]) post_order_traversal() []T + post_order_traversal traverses the BST in post order, and returns the result in an array. +fn (bst &BSTree[T]) pre_order_traversal() []T + pre_order_traversal traverses the BST in pre order, and returns the result as an array. +fn (bst &BSTree[T]) to_left(value T) !T + to_left returns the value of the node to the left of the node with `value` specified if it exists, otherwise the a false value is returned. + + An example of usage can be the following one + ```v + left_value, exist := bst.to_left(10) + ``` +fn (bst &BSTree[T]) to_right(value T) !T + to_right return the value of the element to the right of the node with `value` specified, if exist otherwise, the boolean value is false An example of usage can be the following one + + ```v + left_value, exist := bst.to_right(10) + ``` +fn (bst &BSTree[T]) max() !T + max return the max element inside the BST. Time complexity O(N) if the BST is not balanced +fn (bst &BSTree[T]) min() !T + min return the minimum element in the BST. Time complexity O(N) if the BST is not balanced. +fn (mut b BloomFilter[T]) add(element T) + adds the element to bloom filter. +fn (b &BloomFilter[T]) exists(element T) bool + checks the element is exists. +fn (l &BloomFilter[T]) @union(r &BloomFilter[T]) !&BloomFilter[T] + @union returns the union of the two bloom filters. +fn (l &BloomFilter[T]) intersection(r &BloomFilter[T]) !&BloomFilter[T] + intersection returns the intersection of bloom filters. +fn (list DoublyLinkedList[T]) is_empty() bool + is_empty checks if the linked list is empty +fn (list DoublyLinkedList[T]) len() int + len returns the length of the linked list +fn (list DoublyLinkedList[T]) first() !T + first returns the first element of the linked list +fn (list DoublyLinkedList[T]) last() !T + last returns the last element of the linked list +fn (mut list DoublyLinkedList[T]) push_back(item T) + push_back adds an element to the end of the linked list +fn (mut list DoublyLinkedList[T]) push_front(item T) + push_front adds an element to the beginning of the linked list +fn (mut list DoublyLinkedList[T]) push_many(elements []T, direction Direction) + push_many adds array of elements to the beginning of the linked list +fn (mut list DoublyLinkedList[T]) pop_back() !T + pop_back removes the last element of the linked list +fn (mut list DoublyLinkedList[T]) pop_front() !T + pop_front removes the last element of the linked list +fn (mut list DoublyLinkedList[T]) insert(idx int, item T) ! + insert adds an element to the linked list at the given index +fn (list &DoublyLinkedList[T]) index(item T) !int + index searches the linked list for item and returns the forward index or none if not found. +fn (mut list DoublyLinkedList[T]) delete(idx int) + delete removes index idx from the linked list and is safe to call for any idx. +fn (list DoublyLinkedList[T]) str() string + str returns a string representation of the linked list +fn (list DoublyLinkedList[T]) array() []T + array returns a array representation of the linked list +fn (mut list DoublyLinkedList[T]) next() ?T + next implements the iter interface to use DoublyLinkedList with V's `for x in list {` loop syntax. +fn (mut list DoublyLinkedList[T]) iterator() DoublyListIter[T] + iterator returns a new iterator instance for the `list`. +fn (mut list DoublyLinkedList[T]) back_iterator() DoublyListIterBack[T] + back_iterator returns a new backwards iterator instance for the `list`. +fn (mut iter DoublyListIterBack[T]) next() ?T + next returns *the previous* element of the list, or `none` when the start of the list is reached. It is called by V's `for x in iter{` on each iteration. +fn (mut iter DoublyListIter[T]) next() ?T + next returns *the next* element of the list, or `none` when the end of the list is reached. It is called by V's `for x in iter{` on each iteration. +fn (list LinkedList[T]) is_empty() bool + is_empty checks if the linked list is empty +fn (list LinkedList[T]) len() int + len returns the length of the linked list +fn (list LinkedList[T]) first() !T + first returns the first element of the linked list +fn (list LinkedList[T]) last() !T + last returns the last element of the linked list +fn (list LinkedList[T]) index(idx int) !T + index returns the element at the given index of the linked list +fn (mut list LinkedList[T]) push(item T) + push adds an element to the end of the linked list +fn (mut list LinkedList[T]) push_many(elements []T) + push adds an array of elements to the end of the linked list +fn (mut list LinkedList[T]) pop() !T + pop removes the last element of the linked list +fn (mut list LinkedList[T]) shift() !T + shift removes the first element of the linked list +fn (mut list LinkedList[T]) insert(idx int, item T) ! + insert adds an element to the linked list at the given index +fn (mut list LinkedList[T]) prepend(item T) + prepend adds an element to the beginning of the linked list (equivalent to insert(0, item)) +fn (list LinkedList[T]) str() string + str returns a string representation of the linked list +fn (list LinkedList[T]) array() []T + array returns a array representation of the linked list +fn (mut list LinkedList[T]) next() ?T + next implements the iteration interface to use LinkedList with V's `for` loop syntax. +fn (mut list LinkedList[T]) iterator() ListIter[T] + iterator returns a new iterator instance for the `list`. +fn (mut iter ListIter[T]) next() ?T + next returns the next element of the list, or `none` when the end of the list is reached. It is called by V's `for x in iter{` on each iteration. +fn (mut heap MinHeap[T]) insert(item T) + insert adds an element to the heap. +fn (mut heap MinHeap[T]) insert_many(elements []T) + insert array of elements to the heap. +fn (mut heap MinHeap[T]) pop() !T + pop removes the top-most element from the heap. +fn (heap MinHeap[T]) peek() !T + peek gets the top-most element from the heap without removing it. +fn (heap MinHeap[T]) len() int + len returns the number of elements in the heap. +fn (queue Queue[T]) is_empty() bool + is_empty checks if the queue is empty +fn (queue Queue[T]) len() int + len returns the length of the queue +fn (queue Queue[T]) peek() !T + peek returns the head of the queue (first element added) +fn (queue Queue[T]) last() !T + last returns the tail of the queue (last element added) +fn (queue Queue[T]) index(idx int) !T + index returns the element at the given index of the queue +fn (mut queue Queue[T]) push(item T) + push adds an element to the tail of the queue +fn (mut queue Queue[T]) pop() !T + pop removes the element at the head of the queue and returns it +fn (queue Queue[T]) str() string + str returns a string representation of the queue +fn (queue Queue[T]) array() []T + array returns a array representation of the queue +fn (mut rb RingBuffer[T]) push(element T) ! + push adds an element to the ring buffer. +fn (mut rb RingBuffer[T]) pop() !T + pop returns the oldest element in the buffer. +fn (mut rb RingBuffer[T]) push_many(elements []T) ! + push_many pushes an array to the buffer. +fn (mut rb RingBuffer[T]) pop_many(n u64) ![]T + pop_many returns `n` elements of the buffer starting with the oldest one. +fn (rb RingBuffer[T]) is_empty() bool + is_empty returns `true` if the ring buffer is empty, `false` otherwise. +fn (rb RingBuffer[T]) is_full() bool + is_full returns `true` if the ring buffer is full, `false` otherwise. +fn (rb RingBuffer[T]) capacity() int + capacity returns the capacity of the ring buffer. +fn (mut rb RingBuffer[T]) clear() + clear empties the ring buffer and all pushed elements. +fn (rb RingBuffer[T]) occupied() int + occupied returns the occupied capacity of the buffer. +fn (rb RingBuffer[T]) remaining() int + remaining returns the remaining capacity of the buffer. +fn (set Set[T]) exists(element T) bool + checks the element is exists. +fn (mut set Set[T]) add(element T) + adds the element to set, if it is not present already. +fn (mut set Set[T]) remove(element T) + removes the element from set. +fn (set Set[T]) pick() !T + pick returns an arbitrary element of set, if set is not empty. +fn (mut set Set[T]) rest() ![]T + rest returns the set consisting of all elements except for the arbitrary element. +fn (mut set Set[T]) pop() !T + pop returns an arbitrary element and deleting it from set. +fn (mut set Set[T]) clear() + delete all elements of set. +fn (l Set[T]) == (r Set[T]) bool + == checks whether the two given sets are equal (i.e. contain all and only the same elements). +fn (set Set[T]) is_empty() bool + is_empty checks whether the set is empty or not. +fn (set Set[T]) size() int + size returns the number of elements in the set. +fn (set Set[T]) copy() Set[T] + copy returns a copy of all the elements in the set. +fn (mut set Set[T]) add_all(elements []T) + add_all adds the whole `elements` array to the set +fn (l Set[T]) @union(r Set[T]) Set[T] + @union returns the union of the two sets. +fn (l Set[T]) intersection(r Set[T]) Set[T] + intersection returns the intersection of sets. +fn (l Set[T]) - (r Set[T]) Set[T] + - returns the difference of sets. +fn (l Set[T]) subset(r Set[T]) bool + subset returns true if the set `r` is a subset of the set `l`. +fn (stack Stack[T]) is_empty() bool + is_empty checks if the stack is empty +fn (stack Stack[T]) len() int + len returns the length of the stack +fn (stack Stack[T]) peek() !T + peek returns the top of the stack +fn (mut stack Stack[T]) push(item T) + push adds an element to the top of the stack +fn (mut stack Stack[T]) pop() !T + pop removes the element at the top of the stack and returns it +fn (stack Stack[T]) str() string + str returns a string representation of the stack +fn (stack Stack[T]) array() []T + array returns a array representation of the stack +enum Direction { + front + back +} +struct AABB { +pub mut: + x f64 + y f64 + width f64 + height f64 +} +struct BSTree[T] { +mut: + root &BSTreeNode[T] = unsafe { 0 } +} + Pure Binary Seach Tree implementation + + Pure V implementation of the Binary Search Tree Time complexity of main operation O(log N) Space complexity O(N) +struct DoublyLinkedList[T] { +mut: + head &DoublyListNode[T] = unsafe { 0 } + tail &DoublyListNode[T] = unsafe { 0 } + // Internal iter pointer for allowing safe modification + // of the list while iterating. TODO: use an option + // instead of a pointer to determine it is initialized. + iter &DoublyListIter[T] = unsafe { 0 } + len int +} + DoublyLinkedList[T] represents a generic doubly linked list of elements, each of type T. +struct DoublyListIter[T] { +mut: + node &DoublyListNode[T] = unsafe { 0 } +} + DoublyListIter[T] is an iterator for DoublyLinkedList. It starts from *the start* and moves forwards to *the end* of the list. It can be used with V's `for x in iter {` construct. One list can have multiple independent iterators, pointing to different positions/places in the list. A DoublyListIter iterator instance always traverses the list from *start to finish*. +struct DoublyListIterBack[T] { +mut: + node &DoublyListNode[T] = unsafe { 0 } +} + DoublyListIterBack[T] is an iterator for DoublyLinkedList. It starts from *the end* and moves backwards to *the start* of the list. It can be used with V's `for x in iter {` construct. One list can have multiple independent iterators, pointing to different positions/places in the list. A DoublyListIterBack iterator instance always traverses the list from *finish to start*. +struct LinkedList[T] { +mut: + head &ListNode[T] = unsafe { 0 } + len int + // Internal iter pointer for allowing safe modification + // of the list while iterating. TODO: use an option + // instead of a pointer to determine if it is initialized. + iter &ListIter[T] = unsafe { 0 } +} +struct ListIter[T] { +mut: + node &ListNode[T] = unsafe { 0 } +} + ListIter[T] is an iterator for LinkedList. It can be used with V's `for x in iter {` construct. One list can have multiple independent iterators, pointing to different positions/places in the list. An iterator instance always traverses the list from start to finish. +struct ListNode[T] { +mut: + data T + next &ListNode[T] = unsafe { 0 } +} +struct MinHeap[T] { +mut: + data []T +} + MinHeap is a binary minimum heap data structure. +struct Quadtree { +pub mut: + perimeter AABB + capacity int + depth int + level int + particles []AABB + nodes []Quadtree +} +fn (mut q Quadtree) create(x f64, y f64, width f64, height f64, capacity int, depth int, level int) Quadtree + create returns a new configurable root node for the tree. +fn (mut q Quadtree) insert(p AABB) + insert recursively adds a particle in the correct index of the tree. +fn (mut q Quadtree) retrieve(p AABB) []AABB + retrieve recursively checks if a particle is in a specific index of the tree. +fn (mut q Quadtree) clear() + clear flushes out nodes and particles from the tree. +fn (q Quadtree) get_nodes() []Quadtree + get_nodes recursively returns the subdivisions the tree has. +struct Queue[T] { +mut: + elements LinkedList[T] +} +struct RingBuffer[T] { +mut: + reader int // index of the tail where data is going to be read + writer int // index of the head where data is going to be written + content []T +} + RingBuffer represents a ring buffer also known as a circular buffer. +struct Set[T] { +mut: + elements map[T]u8 +} +struct Stack[T] { +mut: + elements []T +} diff --git a/aiprompts/details/heroscript_internal.md b/aiprompts/details/heroscript_internal.md new file mode 100644 index 00000000..936e69af --- /dev/null +++ b/aiprompts/details/heroscript_internal.md @@ -0,0 +1,79 @@ + +## how internally a heroscript gets parsed for params + +- example to show how a heroscript gets parsed in action with params +- params are part of action object + +```heroscript +example text to parse (heroscript) + +id:a1 name6:aaaaa +name:'need to do something 1' +description: + ' + ## markdown works in it + description can be multiline + lets see what happens + + - a + - something else + + ### subtitle + ' + +name2: test +name3: hi +name10:'this is with space' name11:aaa11 + +name4: 'aaa' + +//somecomment +name5: 'aab' +``` + +the params are part of the action and are represented as follow for the above: + +```vlang +Params{ + params: [Param{ + key: 'id' + value: 'a1' + }, Param{ + key: 'name6' + value: 'aaaaa' + }, Param{ + key: 'name' + value: 'need to do something 1' + }, Param{ + key: 'description' + value: '## markdown works in it + + description can be multiline + lets see what happens + + - a + - something else + + ### subtitle + ' + }, Param{ + key: 'name2' + value: 'test' + }, Param{ + key: 'name3' + value: 'hi' + }, Param{ + key: 'name10' + value: 'this is with space' + }, Param{ + key: 'name11' + value: 'aaa11' + }, Param{ + key: 'name4' + value: 'aaa' + }, Param{ + key: 'name5' + value: 'aab' + }] + } +``` \ No newline at end of file diff --git a/aiprompts/docker.md b/aiprompts/docker.md new file mode 120000 index 00000000..3894e50f --- /dev/null +++ b/aiprompts/docker.md @@ -0,0 +1 @@ +../crystallib/virt/docker/readme.md \ No newline at end of file diff --git a/aiprompts/gittools.md b/aiprompts/gittools.md new file mode 120000 index 00000000..877777d4 --- /dev/null +++ b/aiprompts/gittools.md @@ -0,0 +1 @@ +../lib/develop/gittools/README.md \ No newline at end of file diff --git a/aiprompts/html_parser.md b/aiprompts/html_parser.md new file mode 100644 index 00000000..6a4a50d1 --- /dev/null +++ b/aiprompts/html_parser.md @@ -0,0 +1,124 @@ +module net.html + +net/html is an **HTML Parser** written in pure V. + +## Usage + +```v +import net.html + +fn main() { + doc := html.parse('

Hello world!

') + tag := doc.get_tags(name: 'h1')[0] //

Hello world!

+ println(tag.name) // h1 + println(tag.content) // Hello world! + println(tag.attributes) // {'class':'title'} + println(tag.str()) //

Hello world!

+} +``` + +More examples found on [`parser_test.v`](parser_test.v) and [`html_test.v`](html_test.v) + +fn parse(text string) DocumentObjectModel + parse parses and returns the DOM from the given text. + + Note: this function converts tags to lowercase. E.g. content is parsed as content. +fn parse_file(filename string) DocumentObjectModel + parse_file parses and returns the DOM from the contents of a file. + + Note: this function converts tags to lowercase. E.g. content is parsed as content. +enum CloseTagType { + in_name + new_tag +} +struct DocumentObjectModel { +mut: + root &Tag = unsafe { nil } + constructed bool + btree BTree + all_tags []&Tag + all_attributes map[string][]&Tag + close_tags map[string]bool // add a counter to see count how many times is closed and parse correctly + attributes map[string][]string + tag_attributes map[string][][]&Tag + tag_type map[string][]&Tag + debug_file os.File +} + The W3C Document Object Model (DOM) is a platform and language-neutral interface that allows programs and scripts to dynamically access and update the content, structure, and style of a document. + + https://www.w3.org/TR/WD-DOM/introduction.html +fn (dom DocumentObjectModel) get_root() &Tag + get_root returns the root of the document. +fn (dom DocumentObjectModel) get_tag(name string) []&Tag + get_tag retrieves all tags in the document that have the given tag name. +fn (dom DocumentObjectModel) get_tags(options GetTagsOptions) []&Tag + get_tags returns all tags stored in the document. +fn (dom DocumentObjectModel) get_tags_by_class_name(names ...string) []&Tag + get_tags_by_class_name retrieves all tags recursively in the document root that have the given class name(s). +fn (dom DocumentObjectModel) get_tag_by_attribute(name string) []&Tag + get_tag_by_attribute retrieves all tags in the document that have the given attribute name. +fn (dom DocumentObjectModel) get_tags_by_attribute(name string) []&Tag + get_tags_by_attribute retrieves all tags in the document that have the given attribute name. +fn (mut dom DocumentObjectModel) get_tags_by_attribute_value(name string, value string) []&Tag + get_tags_by_attribute_value retrieves all tags in the document that have the given attribute name and value. +fn (mut dom DocumentObjectModel) get_tag_by_attribute_value(name string, value string) []&Tag + get_tag_by_attribute_value retrieves all tags in the document that have the given attribute name and value. +struct GetTagsOptions { +pub: + name string +} +struct Parser { +mut: + dom DocumentObjectModel + lexical_attributes LexicalAttributes = LexicalAttributes{ + current_tag: &Tag{} + } + filename string = 'direct-parse' + initialized bool + tags []&Tag + debug_file os.File +} + Parser is responsible for reading the HTML strings and converting them into a `DocumentObjectModel`. +fn (mut parser Parser) add_code_tag(name string) + This function is used to add a tag for the parser ignore it's content. For example, if you have an html or XML with a custom tag, like `