From 67e8f2dea0d4eec20a839d47f1fa6302a4a5f733 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 19 Aug 2024 00:23:02 -0400 Subject: Move docs into one folder --- docs/README.md | 78 ++++ docs/arrays.md | 797 +++++++++++++++++++++++++++++++ docs/booleans.md | 65 +++ docs/channels.md | 181 +++++++ docs/enums.md | 88 ++++ docs/integers.md | 368 +++++++++++++++ docs/nums.md | 1374 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ docs/pointers.md | 133 ++++++ docs/ranges.md | 65 +++ docs/sets.md | 356 ++++++++++++++ docs/structs.md | 39 ++ docs/tables.md | 294 ++++++++++++ docs/text.md | 778 +++++++++++++++++++++++++++++++ docs/threads.md | 111 +++++ 14 files changed, 4727 insertions(+) create mode 100644 docs/README.md create mode 100644 docs/arrays.md create mode 100644 docs/booleans.md create mode 100644 docs/channels.md create mode 100644 docs/enums.md create mode 100644 docs/integers.md create mode 100644 docs/nums.md create mode 100644 docs/pointers.md create mode 100644 docs/ranges.md create mode 100644 docs/sets.md create mode 100644 docs/structs.md create mode 100644 docs/tables.md create mode 100644 docs/text.md create mode 100644 docs/threads.md (limited to 'docs') diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..55bc63ba --- /dev/null +++ b/docs/README.md @@ -0,0 +1,78 @@ +# Documentation + +This is an overview of the documentation on Tomo. + +## Topics + +A few topics that are documented: + +- [Compilation Pipeline](compilation.md) +- [Libraries/Modules](libraries.md) +- [Special Methods](metamethods.md) +- [Namespacing](namespacing.md) +- [Operator Overloading](operators.md) + +## Types + +Information about Tomo's built-in types can be found here: + +- [Arrays](arrays.md) +- [Booleans](booleans.md) +- [Channels](channels.md) +- [Enums](enums.md) +- [Floating point numbers](nums.md) +- [Integer Ranges](ranges.md) +- [Integers](integers.md) +- [Sets](sets.md) +- [Structs](structs.md) +- [Tables](tables.md) +- [Text](text.md) +- [Threads](threads.md) + +## Built-in Functions + +### `say` + +**Description:** +Prints a message to the console. + +**Usage:** +```markdown +say(text:Text) -> Void +``` + +**Parameters:** + +- `text`: The text to print. + +**Returns:** +Nothing. + +**Example:** +```markdown +say("Hello world!") +``` + +--- + +### `fail` + +**Description:** +Prints a message to the console, aborts the program, and prints a stack trace. + +**Usage:** +```markdown +fail(message:Text) -> Abort +``` + +**Parameters:** + +- `message`: The error message to print. + +**Returns:** +Nothing, aborts the program. + +**Example:** +```markdown +fail("Oh no!") +``` diff --git a/docs/arrays.md b/docs/arrays.md new file mode 100644 index 00000000..0453feb6 --- /dev/null +++ b/docs/arrays.md @@ -0,0 +1,797 @@ +# Arrays + +Tomo supports arrays as a container type that holds a list of elements of any +type in a compact format. Arrays are immutable by default, but use +copy-on-write semantics to efficiently mutate in place when possible. **Arrays +are 1-indexed**, which means the first item in the array has index `1`. + +## Syntax + +Arrays are written using square brackets and a list of comma-separated elements: + +```tomo +nums := [10, 20, 30] +``` + +Each element must have the same type (or be easily promoted to the same type). If +you want to have an empty array, you must specify what type goes inside the array +like this: + +```tomo +empty := [:Int] +``` + +For type annotations, an array that holds items with type `T` is written as `[T]`. + +### Array Comprehensions + +Arrays can also use comprehensions, where you specify how to dynamically create +all the elements by iteration instead of manually specifying each: + +```tomo +>> [i*10 for i in 3:to(8)] += [30, 40, 50, 60, 70, 80] +>> [i*10 for i in 3:to(8) if i != 4] += [30, 50, 60, 70, 80] +``` + +Comprehensions can be combined with regular items or other comprehensions: + +```tomo +>> [-1, i*10 for i in 3:to(8), i for i in 3] += [-1, 30, 40, 50, 60, 70, 80, 1, 2, 3] +``` + +## Length + +Array length can be accessed by the `.length` field: + +```tomo +>> [10, 20, 30].length += 3 +``` + +## Indexing + +Array values are accessed using square bracket indexing. Since arrays are +1-indexed, the index `1` corresponds to the first item in the array. Negative +indices are used to refer to items from the back of the array, so `-1` is the +last item, `-2` is the second-to-last, and so on. + +```tomo +arr := [10, 20, 30, 40] +>> arr[1] += 10 + +>> arr[2] += 20 + +>> arr[-1] += 40 + +>> arr[-2] += 30 +``` + +If an array index of `0` or any value larger than the length of the array is +used, it will trigger a runtime error that will print what the invalid array +index was, the length of the array, and a stack trace. As a performance +operation, if array bounds checking proves to be a performance hot spot, you +can explicitly disable bounds checking by adding `arr[i; unchecked]` to the +array access. + +## Iteration + +You can iterate over the items in an array like this: + +```tomo +for item in array: + ... + +for i, item in array: + ... +``` + +Array iteration operates over the value of the array when the loop began, so +modifying the array during iteration is safe and will not result in the loop +iterating over any of the new values. + +## Concatenation + +Arrays can be concatenated with the `++` operator, which returns an array that +has the items from one appended to the other. This should not be confused with +the addition operator `+`, which does not work with arrays. + +```tomo +>> [1, 2] ++ [3, 4] += [1, 2, 3, 4] +``` + +## Array Methods + +### `binary_search` + +**Description:** +Performs a binary search on a sorted array. + +**Usage:** +```markdown +binary_search(arr: [T], by=T.compare) -> Int +``` + +**Parameters:** + +- `arr`: The sorted array to search. +- `by`: The comparison function used to determine order. If not specified, the + default comparison function for the item type will be used. + +**Returns:** +Assuming the input array is sorted according to the given comparison function, +return the index where the given item would be inserted to maintain the sorted +order. + +**Example:** +```markdown +>> [1, 3, 5, 7, 9]:binary_search(5) += 3 + +>> [1, 3, 5, 7, 9]:binary_search(-999) += 1 + +>> [1, 3, 5, 7, 9]:binary_search(999) += 6 +``` + +--- + +### `by` + +**Description:** +Creates a new array with elements spaced by the specified step value. + +**Usage:** +```markdown +by(arr: [T], step: Int) -> [T] +``` + +**Parameters:** + +- `arr`: The original array. +- `step`: The step value for selecting elements. + +**Returns:** +A new array with every `step`-th element from the original array. + +**Example:** +```markdown +>> [1, 2, 3, 4, 5, 6]:by(2) += [1, 3, 5] +``` + +--- + +### `clear` + +**Description:** +Clears all elements from the array. + +**Usage:** +```markdown +clear(arr: & [T]) -> Void +``` + +**Parameters:** + +- `arr`: The mutable reference to the array to be cleared. + +**Returns:** +Nothing. + +**Example:** +```markdown +>> my_array:clear() +``` + +--- + +### `counts` + +**Description:** +Counts the occurrences of each element in the array. + +**Usage:** +```markdown +counts(arr: [T]) -> {T: Int} +``` + +**Parameters:** + +- `arr`: The array to count elements in. + +**Returns:** +A table mapping each element to its count. + +**Example:** +```markdown +>> [10, 20, 30, 30, 30]:counts() += {10: 1, 20: 1, 30: 3} +``` + +--- + +### `find` + +**Description:** +Finds the index of the first occurrence of an element. + +**Usage:** +```markdown +find(arr: [T]) -> Int +``` + +**Parameters:** + +- `arr`: The array to search through. + +**Returns:** +The index of the first occurrence or `0` if not found. + +**Example:** +```markdown +>> [10, 20, 30, 40, 50]:find(20) += 2 + +>> [10, 20, 30, 40, 50]:find(9999) += 0 +``` + +--- + +### `first` + +**Description:** +Find the first item that matches a predicate function. + +**Usage:** +```markdown +first(arr: [T], predicate: func(item:&T)->Bool) -> Int +``` + +**Parameters:** + +- `arr`: The array to search through. +- `predicate`: A function that returns `yes` if the item should be returned or + `no` if it should not. + +**Returns:** +Returns a pointer to the first item that matches the given predicate, or null +if no match is found. + +**Example:** +```markdown +when nums:find(func(i:&Int): i:is_prime()) is @prime: + say("Got a prime: $prime") +else: + say("No primes") +``` + +--- + +### `from` + +**Description:** +Returns a slice of the array starting from a specified index. + +**Usage:** +```markdown +from(arr: [T], first: Int) -> [T] +``` + +**Parameters:** + +- `arr`: The original array. +- `first`: The index to start from. + +**Returns:** +A new array starting from the specified index. + +**Example:** +```markdown +>> [10, 20, 30, 40, 50]:from(3) += [30, 40, 50] +``` + +--- + +### `has` + +**Description:** +Checks if the array has any elements. + +**Usage:** +```markdown +has(arr: [T]) -> Bool +``` + +**Parameters:** + +- `arr`: The array to check. + +**Returns:** +`yes` if the array has elements, `no` otherwise. + +**Example:** +```markdown +>> [10, 20, 30]:has(20) += yes +``` + +--- + +### `heap_pop` + +**Description:** +Removes and returns the top element of a heap. By default, this is the +*minimum* value in the heap. + +**Usage:** +```markdown +heap_pop(arr: & [T], by=T.compare) -> T +``` + +**Parameters:** + +- `arr`: The mutable reference to the heap. +- `by`: The comparison function used to determine order. If not specified, the + default comparison function for the item type will be used. + +**Returns:** +The removed top element of the heap. + +**Example:** +```markdown +>> my_heap := [30, 10, 20] +>> my_heap:heapify() +>> my_heap:heap_pop() += 10 +``` + +--- + +### `heap_push` + +**Description:** +Adds an element to the heap and maintains the heap property. By default, this +is a *minimum* heap. + +**Usage:** +```markdown +heap_push(arr: & [T], item: T, by=T.compare) -> Void +``` + +**Parameters:** + +- `arr`: The mutable reference to the heap. +- `item`: The item to be added. +- `by`: The comparison function used to determine order. If not specified, the + default comparison function for the item type will be used. + +**Returns:** +Nothing. + +**Example:** +```markdown +>> my_heap:heap_push(10) +``` + +--- + +### `heapify` + +**Description:** +Converts an array into a heap. + +**Usage:** +```markdown +heapify(arr: & [T], by=T.compare) -> Void +``` + +**Parameters:** + +- `arr`: The mutable reference to the array to be heapified. +- `by`: The comparison function used to determine order. If not specified, the + default comparison function for the item type will be used. + +**Returns:** +Nothing. + +**Example:** +```markdown +>> my_heap := [30, 10, 20] +>> my_heap:heapify() +``` + +--- + +### `insert` + +**Description:** +Inserts an element at a specified position in the array. + +**Usage:** +```markdown +insert(arr: & [T], item: T, at: Int = 0) -> Void +``` + +**Parameters:** + +- `arr`: The mutable reference to the array. +- `item`: The item to be inserted. +- `at`: The index at which to insert the item (default is `0`). Since indices + are 1-indexed and negative indices mean "starting from the back", an index of + `0` means "after the last item". + +**Returns:** +Nothing. + +**Example:** +```markdown +>> arr := [10, 20] +>> arr:insert(30) +>> arr += [10, 20, 30] + +>> arr:insert(999, at=2) +>> arr += [10, 999, 20, 30] +``` + +--- + +### `insert_all` + +**Description:** +Inserts an array of items at a specified position in the array. + +**Usage:** +```markdown +insert_all(arr: & [T], items: [T], at: Int = 0) -> Void +``` + +**Parameters:** + +- `arr`: The mutable reference to the array. +- `items`: The items to be inserted. +- `at`: The index at which to insert the item (default is `0`). Since indices + are 1-indexed and negative indices mean "starting from the back", an index of + `0` means "after the last item". + +**Returns:** +Nothing. + +**Example:** +```markdown +arr := [10, 20] +arr:insert_all([30, 40]) +>> arr += [10, 20, 30, 40] + +arr:insert_all([99, 100], at=2) +>> arr += [10, 99, 100, 20, 30, 40] +``` + +--- + +### `random` + +**Description:** +Selects a random element from the array. + +**Usage:** +```markdown +random(arr: [T]) -> T +``` + +**Parameters:** + +- `arr`: The array from which to select a random element. + +**Returns:** +A random element from the array. + +**Example:** +```markdown +>> [10, 20, 30]:random() += 20 +``` + +--- + +### `remove_at` + +**Description:** +Removes elements from the array starting at a specified index. + +**Usage:** +```markdown +remove_at(arr: & [T], at: Int = -1, count: Int = 1) -> Void +``` + +**Parameters:** + +- `arr`: The mutable reference to the array. +- `at`: The index at which to start removing elements (default is `-1`, which means the end of the array). +- `count`: The number of elements to remove (default is `1`). + +**Returns:** +Nothing. + +**Example:** +```markdown +arr := [10, 20, 30, 40, 50] +arr:remove_at(2) +>> arr += [10, 30, 40, 50] + +arr:remove_at(2, count=2) +>> arr += [10, 50] +``` + +--- + +### `remove_item` + +**Description:** +Removes all occurrences of a specified item from the array. + +**Usage:** +```markdown +remove_item(arr: & [T], item: T, max_count: Int = -1) -> Void +``` + +**Parameters:** + +- `arr`: The mutable reference to the array. +- `item`: The item to be removed. +- `max_count`: The maximum number of occurrences to remove (default is `-1`, meaning all occurrences). + +**Returns:** +Nothing. + +**Example:** +```markdown +arr := [10, 20, 10, 20, 30] +arr:remove_item(10) +>> arr += [20, 20, 30] + +arr:remove_item(20, max_count=1) +>> arr += [20, 30] +``` + +--- + +### `reversed` + +**Description:** +Returns a reversed slice of the array. + +**Usage:** +```markdown +reversed(arr: [T]) -> [T] +``` + +**Parameters:** + +- `arr`: The array to be reversed. + +**Returns:** +A slice of the array with elements in reverse order. + +**Example:** +```markdown +>> [10, 20, 30]:reversed() += [30, 20, 10] +``` + +--- + +### `sample` + +**Description:** +Selects a sample of elements from the array, optionally with weighted +probabilities. + +**Usage:** +```markdown +sample(arr: [T], count: Int, weights: [Num]) -> [T] +``` + +**Parameters:** + +- `arr`: The array to sample from. +- `count`: The number of elements to sample. +- `weights`: The probability weights for each element in the array. These + values do not need to add up to any particular number, they are relative + weights. If no weights are provided, the default is equal probabilities. + Negative, infinite, or NaN weights will cause a runtime error. If the number of + weights given is less than the length of the array, elements from the rest of + the array are considered to have zero weight. + +**Returns:** +A list of sampled elements from the array. + +**Example:** +```markdown +>> [10, 20, 30]:sample(2, weights=[90%, 5%, 5%]) += [10, 10] +``` + +--- + +### `shuffle` + +**Description:** +Shuffles the elements of the array in place. + +**Usage:** +```markdown +shuffle(arr: & [T]) -> Void +``` + +**Parameters:** + +- `arr`: The mutable reference to the array to be shuffled. + +**Returns:** +Nothing. + +**Example:** +```markdown +>> arr:shuffle() +``` + +--- + +### `shuffled` + +**Description:** +Creates a new array with elements shuffled. + +**Usage:** +```markdown +shuffled(arr: [T]) -> [T] +``` + +**Parameters:** + +- `arr`: The array to be shuffled. + +**Returns:** +A new array with shuffled elements. + +**Example:** +```markdown +>> [10, 20, 30, 40]:shuffled() += [40, 10, 30, 20] +``` + +--- + +### `sort` + +**Description:** +Sorts the elements of the array in place in ascending order (small to large). + +**Usage:** +```markdown +sort(arr: & [T], by=T.compare) -> Void +``` + +**Parameters:** + +- `arr`: The mutable reference to the array to be sorted. +- `by`: The comparison function used to determine order. If not specified, the + default comparison function for the item type will be used. + +**Returns:** +Nothing. + +**Example:** +```markdown +arr := [40, 10, -30, 20] +arr:sort() +>> arr += [-30, 10, 20, 40] + +arr:sort(func(a,b:&Int): a:abs() <> b:abs()) +>> arr += [10, 20, -30, 40] +``` + +--- + +### `sorted` + +**Description:** +Creates a new array with elements sorted. + +**Usage:** +```markdown +sorted(arr: [T], by=T.compare) -> [T] +``` + +**Parameters:** + +- `arr`: The array to be sorted. +- `by`: The comparison function used to determine order. If not specified, the + default comparison function for the item type will be used. + +**Returns:** +A new array with sorted elements. + +**Example:** +```markdown +>> [40, 10, -30, 20]:sorted() += [-30, 10, 20, 40] + +>> [40, 10, -30, 20]:sorted(func(a,b:&Int): a:abs() <> b:abs()) += [10, 20, -30, 40] +``` + +--- + +### `to` + +**Description:** +Returns a slice of the array from the start of the original array up to a specified index (inclusive). + +**Usage:** +```markdown +to(arr: [T], last: Int) -> [T] +``` + +**Parameters:** + +- `arr`: The original array. +- `last`: The index up to which elements should be included. + +**Returns:** +A new array containing elements from the start up to the specified index. + +**Example:** +```markdown +>> [10, 20, 30, 40, 50]:to(3) += [10, 20, 30] + +>> [10, 20, 30, 40, 50]:to(-2) += [10, 20, 30, 40] +``` + +--- + +### `unique` + +**Description:** +Returns a Set that contains the unique elements of the array. + +**Usage:** +```markdown +unique(arr: [T]) -> {T} +``` + +**Parameters:** + +- `arr`: The array to process. + +**Returns:** +A set containing only unique elements from the array. + +**Example:** +```markdown +>> [10, 20, 10, 10, 30]:unique() += {10, 20, 30} +``` diff --git a/docs/booleans.md b/docs/booleans.md new file mode 100644 index 00000000..7d2c29a4 --- /dev/null +++ b/docs/booleans.md @@ -0,0 +1,65 @@ +# Boolean Values + +Boolean values have the type `Bool` and can be either `yes` ("true") or `no` +("false"). + +# Boolean Functions + +This documentation provides details on boolean functions available in the API. + +## `from_text` + +**Description:** +Converts a string representation of a boolean value into a boolean. Acceptable +boolean values are case-insensitive variations of `yes`/`no`, `y`/`n`, +`true`/`false`, `on`/`off`. + +**Usage:** +```tomo +from_text(text: Text, success: Bool = !&Bool) -> Bool +``` + +**Parameters:** + +- `text`: The string containing the boolean value. +- `success`: If provided, this boolean value reference will be set to `yes` if the given text is a recognizable boolean value or `no` otherwise. + +**Returns:** +`yes` if the string matches a recognized truthy boolean value; otherwise return `no`. + +**Example:** +```tomo +>> Bool.from_text("yes") += yes +>> Bool.from_text("no") += no +>> success := yes +>> Bool.from_text("???", &success) += no +>> success += no +``` + +--- + +## `random` + +**Description:** +Generates a random boolean value based on a specified probability. + +**Usage:** +```tomo +random(p: Float = 0.5) -> Bool +``` + +**Parameters:** + +- `p`: The probability (between `0` and `1`) of returning `yes`. Default is `0.5`. + +**Returns:** +`yes` with probability `p`, and `no` with probability `1 - p`. + +**Example:** +```tomo +>> Bool.random(70%) // yes (with 70% probability) +``` diff --git a/docs/channels.md b/docs/channels.md new file mode 100644 index 00000000..88cb46ff --- /dev/null +++ b/docs/channels.md @@ -0,0 +1,181 @@ +# Channels + +Channels are a thread-safe message queue for communicating between threads, +although they can also be used as a general-purpose queue. + +## Syntax + +The syntax to create a channel is `|:T|`, where `T` is the type that will be +passed through the channel. You can also specify a maximum size for the +channel, which will cause giving to block until the recipient has gotten from +the channel if the maximum size is reached. + +```tomo +channel := |:Int| +channel:give(10) +channel:give(20) +>> channel:get() += 10 +>> channel:get() += 20 + +small_channel := |:Int; max_size=5| +``` + +## Channel Methods + +### `give` + +**Description:** +Adds an item to the channel. + +**Usage:** +```markdown +give(channel:|T|, item: T, where: Where = Where.End) -> Void +``` + +**Parameters:** + +- `channel`: The channel to which the item will be added. +- `item`: The item to add to the channel. +- `where`: Where to put the item (at the `Start` or `End` of the queue). + `Anywhere` defaults to `End`. + +**Returns:** +Nothing. + +**Example:** +```markdown +>> channel:give("Hello") +``` + +--- + +### `give_all` + +**Description:** +Adds multiple items to the channel. + +**Usage:** +```markdown +give_all(channel:|T|, items: [T], where: Where = Where.End) -> Void +``` + +**Parameters:** + +- `channel`: The channel to which the items will be added. +- `items`: The array of items to add to the channel. +- `where`: Where to put the items (at the `Start` or `End` of the queue). + `Anywhere` defaults to `End`. + +**Returns:** +Nothing. + +**Example:** +```markdown +>> channel:give_all([1, 2, 3]) +``` + +--- + +### `get` + +**Description:** +Removes and returns an item from the channel. If the channel is empty, it waits until an item is available. + +**Usage:** +```markdown +get(channel:|T|, where: Where = Where.Start) -> T +``` + +**Parameters:** + +- `channel`: The channel from which to remove an item. +- `where`: Where to get the item from (from the `Start` or `End` of the queue). + `Anywhere` defaults to `Start`. + +**Returns:** +The item removed from the channel. + +**Example:** +```markdown +>> channel:peek() += "Hello" +``` + +--- + +### `peek` + +**Description:** +Returns the next item that will come out of the channel, but without removing +it. If the channel is empty, it waits until an item is available. + +**Usage:** +```markdown +peek(channel:|T|, where: Where = Where.Start) -> T +``` + +**Parameters:** + +- `channel`: The channel from which to remove an item. +- `where`: Which end of the channel to peek from (the `Start` or `End`). + `Anywhere` defaults to `Start`. + +**Returns:** +The item removed from the channel. + +**Example:** +```markdown +>> channel:get() += "Hello" +``` + +--- + +### `clear` + +**Description:** +Removes all items from the channel. + +**Usage:** +```markdown +clear(channel:|T|) -> Void +``` + +**Parameters:** + +- `channel`: The mutable reference to the channel. + +**Returns:** +Nothing. + +**Example:** +```markdown +>> channel:clear() +``` + +--- + +### `view` + +**Description:** +Returns a list of all items currently in the channel without removing them. + +**Usage:** +```markdown +channel:view() -> [T] +``` + +**Parameters:** + +- `channel`: The channel to view. + +**Returns:** +An array of items currently in the channel. + +**Example:** +```markdown +>> channel:view() += [1, 2, 3] +``` diff --git a/docs/enums.md b/docs/enums.md new file mode 100644 index 00000000..4123fbc4 --- /dev/null +++ b/docs/enums.md @@ -0,0 +1,88 @@ +# Enums + +Tomo supports tagged enumerations, also known as "sum types." Users +can define their own using the `enum` keyword: + +```tomo +enum VariousThings(AnInteger(i:Int), TwoWords(word1, word2:Text), Nothing) + +... + +a := VariousThings.AnInteger(5) +b := VariousThings.TwoWords("one", "two") +c := VariousThings.Nothing +``` + +## Pattern Matching + +The values inside an enum can be accessed with pattern matching: + +```tomo +when x is AnInteger(i): + say("It was $i") +is TwoWords(x, y): + say("It was $x and $y") +is Nothing: + say("It was nothing") +``` + +Pattern matching blocks are always checked for exhaustiveness, but you can add +an `else` block to handle all unmatched patterns. + +## Tag Checking + +Tags can also be quickly checked using the `.TagName` field: + +```tomo +>> a.AnInteger += yes +>> a.TwoWords += no +``` + +## Reducing Boilerplate + +There are three main areas where we can easily reduce the amount of boilerplate +around enums. We don't need to type `VariousThings.` in front of enum values +when we already know what type of enum we're dealing with. This means that we +don't need the name of the type for pattern matching (because we can infer the +type of the expression being matched). We also don't need the name of the type +when calling a function with an enum argument, nor when returning an enum value +from a function with an explicit return type: + +```tomo +enum ArgumentType(AnInt(x:Int), SomeText(text:Text)) +enum ReturnType(Nothing, AnInt(x:Int)) + +func increment(arg:ArgumentType)->ReturnType: + when arg is AnInt(x): + return AnInt(x + 1) + is SomeText: + return Nothing + +... + +>> increment(AnInt(5)) += ReturnType.AnInt(6) +>> increment(SomeText("HI")) += ReturnType.Nothiing +``` + +This lets us have overlapping tag names for different types, but smartly infer +which enum's value is being created when we know what we're expecting to get. +This also works for variable assignment to a variable whose type is already +known. + +## Namespacing + +Enums can also define their own methods and variables inside their namespace: + +```tomo +enum VariousThings(AnInteger(i:Int), TwoWords(word1, word2:Text), Nothing): + meaningful_thing := AnInteger(42) + func doop(v:VariousThings): + say("$v") +``` + +Functions defined in an enum's namespace can be invoked as methods with `:` if +the first argument is the enum's type or a pointer to one (`vt:doop()`). diff --git a/docs/integers.md b/docs/integers.md new file mode 100644 index 00000000..653322fd --- /dev/null +++ b/docs/integers.md @@ -0,0 +1,368 @@ +# Integers + +Tomo has five types of integers: + +- `Int`: the default integer type, which uses an efficient tagged 29-bit + integer value for small numbers, and falls back to a bigint implementation + when values are too large to fit in 29-bits. The bigint implementation uses + the GNU MP library. These integers are fast for small numbers and guaranteed + to always be correct and never overflow. +- `Int8`/`Int16`/`Int32`/`Int64`: Fixed-size integers that take up `N` bits. + These integers must be manually specified with their suffix (e.g. `5_i64`) + and are subject to overflowing. If an overflow occurs, a runtime error will + be raised. + +Conversion between integer types can be done by calling the target type as a +function: `Int32(x)`. For fixed-width types, the conversion function also +accepts a second parameter, `truncate`. If `truncate` is `no` (the default), +conversion will create a runtime error if the value is too large to fit in the +target type. If `truncate` is `yes`, then the resulting value will be a +truncated form of the input value. + +Integers support the standard math operations (`x+y`, `x-y`, `x*y`, `x/y`) as +well as powers/exponentiation (`x^y`), modulus (`x mod y` and `x mod1 y`), and +bitwise operations: `x and y`, `x or y`, `x xor y`, `x << y`, and `x >> y`. The +operators `and`, `or`, and `xor` are _bitwise_, not logical operators. + +# Integer Functions + +Each integer type has its own version of the following functions. Functions +can be called either on the type itself: `Int.sqrt(x)` or as a method call: +`x:sqrt()`. Method call syntax is preferred. + +## `format` + +**Description:** +Formats an integer as a string with a specified number of digits. + +**Usage:** +```tomo +format(i: Int, digits: Int = 0) -> Text +``` + +**Parameters:** + +- `i`: The integer to be formatted. +- `digits`: The minimum number of digits to which the integer should be padded. Default is `0`. + +**Returns:** +A string representation of the integer, padded to the specified number of digits. + +**Example:** +```tomo +>> 42:format(digits=5) += "00042" +``` + +--- + +## `hex` + +**Description:** +Converts an integer to its hexadecimal representation. + +**Usage:** +```tomo +hex(i: Int, digits: Int = 0, uppercase: Bool = yes, prefix: Bool = yes) -> Text +``` + +**Parameters:** + +- `i`: The integer to be converted. +- `digits`: The minimum number of digits in the output string. Default is `0`. +- `uppercase`: Whether to use uppercase letters for hexadecimal digits. Default is `yes`. +- `prefix`: Whether to include a "0x" prefix. Default is `yes`. + +**Returns:** +The hexadecimal string representation of the integer. + +**Example:** +```tomo +>> 255:hex(digits=4, uppercase=yes, prefix=yes) += "0x00FF" +``` + +--- + +## `octal` + +**Description:** +Converts an integer to its octal representation. + +**Usage:** +```tomo +octal(i: Int, digits: Int = 0, prefix: Bool = yes) -> Text +``` + +**Parameters:** + +- `i`: The integer to be converted. +- `digits`: The minimum number of digits in the output string. Default is `0`. +- `prefix`: Whether to include a "0o" prefix. Default is `yes`. + +**Returns:** +The octal string representation of the integer. + +**Example:** +```tomo +>> 64:octal(digits=4, prefix=yes) += "0o0100" +``` + +--- + +## `random` + +**Description:** +Generates a random integer between the specified minimum and maximum values. + +**Usage:** +```tomo +random(min: Int, max: Int) -> Int +``` + +**Parameters:** + +- `min`: The minimum value of the range. +- `max`: The maximum value of the range. + +**Returns:** +A random integer between `min` and `max` (inclusive). + +**Example:** +```tomo +>> Int.random(1, 100) += 47 +``` + +--- + +## `from_text` + +**Description:** +Converts a text representation of an integer into an integer. + +**Usage:** +```tomo +from_text(text: Text, the_rest: Text = "!&Text") -> Int +``` + +**Parameters:** + +- `text`: The string containing the integer. +- `the_rest`: If non-null, this pointer will be set to point to any unparseable text after the integer. + +**Returns:** +The integer represented by the string. + +**Example:** +```tomo +>> from_text("123") += 123 +>> from_text("0xFF") += 255 +``` + +--- + +## `to` + +**Description:** +Creates an inclusive range of integers between the specified start and end values. + +**Usage:** +```tomo +to(from: Int, to: Int) -> Range +``` + +**Parameters:** + +- `from`: The starting value of the range. +- `to`: The ending value of the range. + +**Returns:** +A range object representing all integers from `from` to `to` (inclusive). + +**Example:** +```tomo +>> 1:to(5) += Range(first=1, last=5, step=1) +``` + +--- + +## `abs` + +**Description:** +Calculates the absolute value of an integer. + +**Usage:** +```tomo +abs(x: Int) -> Int +``` + +**Parameters:** + +- `x`: The integer whose absolute value is to be calculated. + +**Returns:** +The absolute value of `x`. + +**Example:** +```tomo +>> -10:abs() += 10 +``` + +--- + +## `sqrt` + +**Description:** +Calculates the square root of an integer. + +**Usage:** +```tomo +sqrt(x: Int) -> Int +``` + +**Parameters:** + +- `x`: The integer whose square root is to be calculated. + +**Returns:** +The integer part of the square root of `x`. + +**Example:** +```tomo +>> 16:sqrt() += 4 +>> 17:sqrt() += 4 +``` + +--- + +## `is_prime` + +**Description:** +Determines if an integer is a prime number. + +**Note:** +This function is _probabilistic_. With the default arguments, the chances of +getting an incorrect answer are astronomically small (on the order of 10^(-30)). +See [the GNU MP docs](https://gmplib.org/manual/Number-Theoretic-Functions#index-mpz_005fprobab_005fprime_005fp) +for more details. + +**Usage:** +```tomo +is_prime(x: Int, reps: Int = 50) -> Bool +``` + +**Parameters:** + +- `x`: The integer to be checked. +- `reps`: The number of repetitions for primality tests. Default is `50`. + +**Returns:** +`yes` if `x` is a prime number, `no` otherwise. + +**Example:** +```tomo +>> 7:is_prime() += yes +>> 6:is_prime() += no +``` + +--- + +## `next_prime` + +**Description:** +Finds the next prime number greater than the given integer. + +**Note:** +This function is _probabilistic_, but the chances of getting an incorrect +answer are astronomically small (on the order of 10^(-30)). +See [the GNU MP docs](https://gmplib.org/manual/Number-Theoretic-Functions#index-mpz_005fprobab_005fprime_005fp) +for more details. + +**Usage:** +```tomo +next_prime(x: Int) -> Int +``` + +**Parameters:** + +- `x`: The integer after which to find the next prime. + +**Returns:** +The next prime number greater than `x`. + +**Example:** +```tomo +>> 11:next_prime() += 13 +``` + +--- + +## `prev_prime` + +**Description:** +Finds the previous prime number less than the given integer. +If there is no previous prime number (i.e. if a number less than `2` is +provided), then the function will create a runtime error. + +**Note:** +This function is _probabilistic_, but the chances of getting an incorrect +answer are astronomically small (on the order of 10^(-30)). +See [the GNU MP docs](https://gmplib.org/manual/Number-Theoretic-Functions#index-mpz_005fprobab_005fprime_005fp) +for more details. + +**Usage:** +```tomo +prev_prime(x: Int) -> Int +``` + +**Parameters:** + +- `x`: The integer before which to find the previous prime. + +**Returns:** +The previous prime number less than `x`. + +**Example:** +```tomo +>> 11:prev_prime() += 7 +``` + +--- + +## `clamped` + +**Description:** +Returns the given number clamped between two values so that it is within +that range. + +**Usage:** +```tomo +clamped(x, low, high: Int) -> Int +``` + +**Parameters:** + +- `x`: The integer to clamp. +- `low`: The lowest value the result can take. +- `high`: The highest value the result can take. + +**Returns:** +The first argument clamped between the other two arguments. + +**Example:** +```tomo +>> 2:clamped(5, 10) += 5 +``` diff --git a/docs/nums.md b/docs/nums.md new file mode 100644 index 00000000..5b258010 --- /dev/null +++ b/docs/nums.md @@ -0,0 +1,1374 @@ +# Nums + +Tomo has two floating point number types: `Num` (64-bit, AKA `double`) and +`Num32` (32-bit, AKA `float`). Num literals can have a decimal point (e.g. +`5.`), a scientific notation suffix (e.g. `1e8`) or a percent sign. Numbers +that end in a percent sign are divided by 100 at compile time (i.e. `5% == +0.05`). + +Nums support the standard math operations (`x+y`, `x-y`, `x*y`, `x/y`) as well as +powers/exponentiation (`x^y`) and modulus (`x mod y` and `x mod1 y`). + +# Num Functions + +Each Num type has its own version of the following functions. Functions can be +called either on the type itself: `Num.sqrt(x)` or as a method call: +`x:sqrt()`. Method call syntax is preferred. + +## Constants + +- **`1_PI`**: \( \frac{1}{\pi} \) +- **`2_PI`**: \( 2 \times \pi \) +- **`2_SQRTPI`**: \( 2 \times \sqrt{\pi} \) +- **`E`**: Base of natural logarithms (\( e \)) +- **`INF`**: Positive infinity +- **`LN10`**: Natural logarithm of 10 +- **`LN2`**: Natural logarithm of 2 +- **`LOG2E`**: Logarithm base 2 of \( e \) +- **`PI`**: Pi (\( \pi \)) +- **`PI_2`**: \( \frac{\pi}{2} \) +- **`PI_4`**: \( \frac{\pi}{4} \) +- **`SQRT1_2`**: \( \sqrt{\frac{1}{2}} \) +- **`SQRT2`**: \( \sqrt{2} \) +- **`TAU`**: Tau (\( 2 \times \pi \)) + +## Functions + +### `abs` + +**Description:** +Calculates the absolute value of a number. + +**Usage:** +```tomo +abs(n: Num) -> Num +``` + +**Parameters:** + +- `n`: The number whose absolute value is to be computed. + +**Returns:** +The absolute value of `n`. + +**Example:** +```tomo +>> -3.5:abs() += 3.5 +``` + +--- + +### `acos` + +**Description:** +Computes the arc cosine of a number. + +**Usage:** +```tomo +acos(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the arc cosine is to be calculated. + +**Returns:** +The arc cosine of `x` in radians. + +**Example:** +```tomo +>> 0.0:acos() // -> (π/2) += 1.5708 +``` + +--- + +### `acosh` + +**Description:** +Computes the inverse hyperbolic cosine of a number. + +**Usage:** +```tomo +acosh(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the inverse hyperbolic cosine is to be calculated. + +**Returns:** +The inverse hyperbolic cosine of `x`. + +**Example:** +```tomo +>> 1.0:acosh() += 0 +``` + +--- + +### `asin` + +**Description:** +Computes the arc sine of a number. + +**Usage:** +```tomo +asin(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the arc sine is to be calculated. + +**Returns:** +The arc sine of `x` in radians. + +**Example:** +```tomo +>> 0.5:asin() // -> (π/6) += 0.5236 +``` + +--- + +### `asinh` + +**Description:** +Computes the inverse hyperbolic sine of a number. + +**Usage:** +```tomo +asinh(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the inverse hyperbolic sine is to be calculated. + +**Returns:** +The inverse hyperbolic sine of `x`. + +**Example:** +```tomo +>> 0.0:asinh() += 0 +``` + +--- + +### `atan2` + +**Description:** +Computes the arc tangent of the quotient of two numbers. + +**Usage:** +```tomo +atan2(x: Num, y: Num) -> Num +``` + +**Parameters:** + +- `x`: The numerator. +- `y`: The denominator. + +**Returns:** +The arc tangent of `x/y` in radians. + +**Example:** +```tomo +>> Num.atan2(1, 1) // -> (π/4) += 0.7854 +``` + +--- + +### `atan` + +**Description:** +Computes the arc tangent of a number. + +**Usage:** +```tomo +atan(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the arc tangent is to be calculated. + +**Returns:** +The arc tangent of `x` in radians. + +**Example:** +```tomo +>> 1.0:atan() // -> (π/4) += 0.7854 +``` + +--- + +### `atanh` + +**Description:** +Computes the inverse hyperbolic tangent of a number. + +**Usage:** +```tomo +atanh(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the inverse hyperbolic tangent is to be calculated. + +**Returns:** +The inverse hyperbolic tangent of `x`. + +**Example:** +```tomo +>> 0.5:atanh() += 0.5493 +``` + +--- + +### `cbrt` + +**Description:** +Computes the cube root of a number. + +**Usage:** +```tomo +cbrt(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the cube root is to be calculated. + +**Returns:** +The cube root of `x`. + +**Example:** +```tomo +>> 27.0:cbrt() += 3 +``` + +--- + +### `ceil` + +**Description:** +Rounds a number up to the nearest integer. + +**Usage:** +```tomo +ceil(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number to be rounded up. + +**Returns:** +The smallest integer greater than or equal to `x`. + +**Example:** +```tomo +>> 3.2:ceil() += 4 +``` + +--- + +### `copysign` + +**Description:** +Copies the sign of one number to another. + +**Usage:** +```tomo +copysign(x: Num, y: Num) -> Num +``` + +**Parameters:** + +- `x`: The number whose magnitude will be copied. +- `y`: The number whose sign will be copied. + +**Returns:** +A number with the magnitude of `x` and the sign of `y`. + +**Example:** +```tomo +>> 3.0:copysign(-1) += -3 +``` + +--- + +### `cos` + +**Description:** +Computes the cosine of a number (angle in radians). + +**Usage:** +```tomo +cos(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The angle in radians. + +**Returns:** +The cosine of `x`. + +**Example:** +```tomo +>> 0.0:cos() += 1 +``` + +--- + +### `cosh` + +**Description:** +Computes the hyperbolic cosine of a number. + +**Usage:** +```tomo +cosh(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the hyperbolic cosine is to be calculated. + +**Returns:** +The hyperbolic cosine of `x`. + +**Example:** +```tomo +>> 0.0:cosh() += 1 +``` + +--- + +### `erf` + +**Description:** +Computes the error function of a number. + +**Usage:** +```tomo +erf(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the error function is to be calculated. + +**Returns:** +The error function of `x`. + +**Example:** +```tomo +>> 0.0:erf() += 0 +``` + +--- + +### `erfc` + +**Description:** +Computes the complementary error function of a number. + +**Usage:** +```tomo +erfc(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the complementary error function is to be calculated. + +**Returns:** +The complementary error function of `x`. + +**Example:** +```tomo +>> 0.0:erfc() += 1 +``` + +--- + +### `exp2` + +**Description:** +Computes \( 2^x \) for a number. + +**Usage:** +```tomo +exp2(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The exponent. + +**Returns:** +The value of \( 2^x \). + +**Example:** +```tomo +>> 3.0:exp2() += 8 +``` + +--- + +### `exp` + +**Description:** +Computes the exponential function \( e^x \) for a number. + +**Usage:** +```tomo +exp(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The exponent. + +**Returns:** +The value of \( e^x \). + +**Example:** +```tomo +>> 1.0:exp() += 2.7183 +``` + +--- + +### `expm1` + +**Description:** +Computes \( e^x - 1 \) for a number. + +**Usage:** +```tomo +expm1(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The exponent. + +**Returns:** +The value of \( e^x - 1 \). + +**Example:** +```tomo +>> 1.0:expm1() += 1.7183 +``` + +--- + +### `fdim` + +**Description:** +Computes the positive difference between two numbers. + +**Usage:** +```tomo +fdim(x: Num, y: Num) -> Num +``` + +**Parameters:** + +- `x`: The first number. +- `y`: The second number. + +**Returns:** +The positive difference \( \max(0, x - y) \). + +**Example:** +```tomo +fd + +>> 5.0:fdim(3) += 2 +``` + +--- + +### `floor` + +**Description:** +Rounds a number down to the nearest integer. + +**Usage:** +```tomo +floor(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number to be rounded down. + +**Returns:** +The largest integer less than or equal to `x`. + +**Example:** +```tomo +>> 3.7:floor() += 3 +``` + +--- + +### `format` + +**Description:** +Formats a number as a string with a specified precision. + +**Usage:** +```tomo +format(n: Num, precision: Int = 0) -> Text +``` + +**Parameters:** + +- `n`: The number to be formatted. +- `precision`: The number of decimal places. Default is `0`. + +**Returns:** +A string representation of the number with the specified precision. + +**Example:** +```tomo +>> 3.14159:format(precision=2) += "3.14" +``` + +--- + +### `from_text` + +**Description:** +Converts a string representation of a number into a floating-point number. + +**Usage:** +```tomo +from_text(text: Text, the_rest: Text = "!&Text") -> Num +``` + +**Parameters:** + +- `text`: The string containing the number. +- `the_rest`: A string indicating what to return if the conversion fails. Default is `"!&Text"`. + +**Returns:** +The number represented by the string. + +**Example:** +```tomo +>> Num.from_text("3.14") += 3.14 +>> Num.from_text("1e3") += 1000 +``` + +--- + +### `hypot` + +**Description:** +Computes the Euclidean norm, \( \sqrt{x^2 + y^2} \), of two numbers. + +**Usage:** +```tomo +hypot(x: Num, y: Num) -> Num +``` + +**Parameters:** + +- `x`: The first number. +- `y`: The second number. + +**Returns:** +The Euclidean norm of `x` and `y`. + +**Example:** +```tomo +>> Num.hypot(3, 4) += 5 +``` + +--- + +### `isfinite` + +**Description:** +Checks if a number is finite. + +**Usage:** +```tomo +isfinite(n: Num) -> Bool +``` + +**Parameters:** + +- `n`: The number to be checked. + +**Returns:** +`yes` if `n` is finite, `no` otherwise. + +**Example:** +```tomo +>> 1.0:isfinite() += yes +>> Num.INF:isfinite() += no +``` + +--- + +### `isinf` + +**Description:** +Checks if a number is infinite. + +**Usage:** +```tomo +isinf(n: Num) -> Bool +``` + +**Parameters:** + +- `n`: The number to be checked. + +**Returns:** +`yes` if `n` is infinite, `no` otherwise. + +**Example:** +```tomo +>> Num.INF:isinf() += yes +>> 1.0:isinf() += no +``` + +--- + +### `isnan` + +**Description:** +Checks if a number is NaN (Not a Number). + +**Usage:** +```tomo +isnan(n: Num) -> Bool +``` + +**Parameters:** + +- `n`: The number to be checked. + +**Returns:** +`yes` if `n` is NaN, `no` otherwise. + +**Example:** +```tomo +>> Num.nan():isnan() += yes +>> 1.0:isnan() += no +``` + +--- + +### `j0` + +**Description:** +Computes the Bessel function of the first kind of order 0. + +**Usage:** +```tomo +j0(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the Bessel function is to be calculated. + +**Returns:** +The Bessel function of the first kind of order 0 of `x`. + +**Example:** +```tomo +>> 0.0:j0() += 1 +``` + +--- + +### `j1` + +**Description:** +Computes the Bessel function of the first kind of order 1. + +**Usage:** +```tomo +j1(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the Bessel function is to be calculated. + +**Returns:** +The Bessel function of the first kind of order 1 of `x`. + +**Example:** +```tomo +>> 0.0:j1() += 0 +``` + +--- + +### `log10` + +**Description:** +Computes the base-10 logarithm of a number. + +**Usage:** +```tomo +log10(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the base-10 logarithm is to be calculated. + +**Returns:** +The base-10 logarithm of `x`. + +**Example:** +```tomo +>> 100.0:log10() += 2 +``` + +--- + +### `log1p` + +**Description:** +Computes \( \log(1 + x) \) for a number. + +**Usage:** +```tomo +log1p(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which \( \log(1 + x) \) is to be calculated. + +**Returns:** +The value of \( \log(1 + x) \). + +**Example:** +```tomo +>> 1.0:log1p() += 0.6931 +``` + +--- + +### `log2` + +**Description:** +Computes the base-2 logarithm of a number. + +**Usage:** +```tomo +log2(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the base-2 logarithm is to be calculated. + +**Returns:** +The base-2 logarithm of `x`. + +**Example:** +```tomo +>> 8.0:log2() += 3 +``` + +--- + +### `log` + +**Description:** +Computes the natural logarithm (base \( e \)) of a number. + +**Usage:** +```tomo +log(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the natural logarithm is to be calculated. + +**Returns:** +The natural logarithm of `x`. + +**Example:** +```tomo +>> Num.E:log() += 1 +``` + +--- + +### `logb` + +**Description:** +Computes the binary exponent (base-2 logarithm) of a number. + +**Usage:** +```tomo +logb(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the binary exponent is to be calculated. + +**Returns:** +The binary exponent of `x`. + +**Example:** +```tomo +>> 8.0:logb() += 3 +``` + +--- + +### `mix` + +**Description:** +Interpolates between two numbers based on a given amount. + +**Usage:** +```tomo +mix(amount: Num, x: Num, y: Num) -> Num +``` + +**Parameters:** + +- `amount`: The interpolation factor (between `0` and `1`). +- `x`: The starting number. +- `y`: The ending number. + +**Returns:** +The interpolated number between `x` and `y` based on `amount`. + +**Example:** +```tomo +>> 0.5:mix(10, 20) += 15 +>> 0.25:mix(10, 20) += 12.5 +``` + +--- + +### `nan` + +**Description:** +Generates a NaN (Not a Number) value. + +**Usage:** +```tomo +nan(tag: Text = "") -> Num +``` + +**Parameters:** + +- `tag`: An optional tag to describe the NaN. Default is an empty string. + +**Returns:** +A NaN value. + +**Example:** +```tomo +>> Num.nan() += NaN +``` + +--- + +### `near` + +**Description:** +Checks if two numbers are approximately equal within specified tolerances. If +two numbers are within an absolute difference or the ratio between the two is +small enough, they are considered near each other. + +**Usage:** +```tomo +near(x: Num, y: Num, ratio: Num = 1e-9, min_epsilon: Num = 1e-9) -> Bool +``` + +**Parameters:** + +- `x`: The first number. +- `y`: The second number. +- `ratio`: The relative tolerance. Default is `1e-9`. +- `min_epsilon`: The absolute tolerance. Default is `1e-9`. + +**Returns:** +`yes` if `x` and `y` are approximately equal within the specified tolerances, `no` otherwise. + +**Example:** +```tomo +>> 1.0:near(1.000000001) += yes + +>> 100.0:near(110, ratio=0.1) += yes + +>> 5.0:near(5.1, min_epsilon=0.1) += yes +``` + +--- + +### `nextafter` + +**Description:** +Computes the next representable value after a given number towards a specified direction. + +**Usage:** +```tomo +nextafter(x: Num, y: Num) -> Num +``` + +**Parameters:** + +- `x`: The starting number. +- `y`: The direction towards which to find the next representable value. + +**Returns:** +The next representable value after `x` in the direction of `y`. + +**Example:** +```tomo +>> 1.0:nextafter(1.1) += 1.0000000000000002 +``` + +--- + +### `random` + +**Description:** +Generates a random floating-point number. + +**Usage:** +```tomo +random() -> Num +``` + +**Parameters:** +None + +**Returns:** +A random floating-point number between 0 and 1. + +**Example:** +```tomo +>> Num.random() += 0.4521 +``` + +--- + +### `rint` + +**Description:** +Rounds a number to the nearest integer, with ties rounded to the nearest even integer. + +**Usage:** +```tomo +rint(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number to be rounded. + +**Returns:** +The nearest integer value of `x`. + +**Example:** +```tomo +>> 3.5:rint() += 4 +>> 2.5:rint() += 2 +``` + +--- + +### `round` + +**Description:** +Rounds a number to the nearest whole number integer. + +**Usage:** +```tomo +round(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number to be rounded. + +**Returns:** +The nearest integer value of `x`. + +**Example:** +```tomo +>> 2.3:round() += 2 +>> 2.7:round() += 3 +``` + +--- + +### `scientific` + +**Description:** +Formats a number in scientific notation with a specified precision. + +**Usage:** +```tomo +scientific(n: Num, precision: Int = 0) -> Text +``` + +**Parameters:** + +- `n`: The number to be formatted. +- `precision`: The number of decimal places. Default is `0`. + +**Returns:** +A string representation of the number in scientific notation with the specified precision. + +**Example:** +```tomo +>> 12345.6789:scientific(precision=2) += "1.23e+04" +``` + +--- + +### `significand` + +**Description:** +Extracts the significand (or mantissa) of a number. + +**Usage:** +```tomo +significand(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number from which to extract the significand. + +**Returns:** +The significand of `x`. + +**Example:** +```tomo +>> 1234.567:significand() += 0.1234567 +``` + +--- + +### `sin` + +**Description:** +Computes the sine of a number (angle in radians). + +**Usage:** +```tomo +sin(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The angle in radians. + +**Returns:** +The sine of `x`. + +**Example:** +```tomo +>> 0.0:sin() += 0 +``` + +--- + +### `sinh` + +**Description:** +Computes the hyperbolic sine of a number. + +**Usage:** +```tomo +sinh(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the hyperbolic sine is to be calculated. + +**Returns:** +The hyperbolic sine of `x`. + +**Example:** +```tomo +>> 0.0:sinh() += 0 +``` + +--- + +### `sqrt` + +**Description:** +Computes the square root of a number. + +**Usage:** +```tomo +sqrt(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the square root is to be calculated. + +**Returns:** +The square root of `x`. + +**Example:** +```tomo +>> 16.0:sqrt() += 4 +``` + +--- + +### `tan` + +**Description:** +Computes the tangent of a number (angle in radians). + +**Usage:** +```tomo +tan(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The angle in radians. + +**Returns:** +The tangent of `x`. + +**Example:** +```tomo +>> 0.0:tan() += 0 +``` + +--- + +### `tanh` + +**Description:** +Computes the hyperbolic tangent of a number. + +**Usage:** +```tomo +tanh(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the hyperbolic tangent is to be calculated. + +**Returns:** +The hyperbolic tangent of `x`. + +**Example:** +```tomo +>> 0.0:tanh() += 0 +``` + +--- + +### `tgamma` + +**Description:** +Computes the gamma function of a number. + +**Usage:** +```tomo +tgamma(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the gamma function is to be calculated. + +**Returns:** +The gamma function of `x`. + +**Example:** +```tomo +>> 1.0:tgamma() += 1 +``` + +--- + +### `trunc` + +**Description:** +Truncates a number to the nearest integer towards zero. + +**Usage:** +```tomo +trunc(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number to be truncated. + +**Returns:** +The integer part of `x` towards zero. + +**Example:** +```tomo +>> 3.7:trunc() += 3 +>> (-3.7):trunc() += -3 +``` + +--- + +### `y0` + +**Description:** +Computes the Bessel function of the second kind of order 0. + +**Usage:** +```tomo +y0(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the Bessel function is to be calculated. + +**Returns:** +The Bessel function of the second kind of order 0 of `x`. + +**Example:** +```tomo +>> 1.0:y0() += -0.7652 +``` + +--- + +### `y1` + +**Description:** +Computes the Bessel function of the second kind of order 1. + +**Usage:** +```tomo +y1(x: Num) -> Num +``` + +**Parameters:** + +- `x`: The number for which the Bessel function is to be calculated. + +**Returns:** +The Bessel function of the second kind of order 1 of `x`. + +**Example:** +```tomo +>> 1.0:y1() += 0.4401 +``` + +--- + +## `clamped` + +**Description:** +Returns the given number clamped between two values so that it is within +that range. + +**Usage:** +```tomo +clamped(x, low, high: Num) -> Num +``` + +**Parameters:** + +- `x`: The number to clamp. +- `low`: The lowest value the result can take. +- `high`: The highest value the result can take. + +**Returns:** +The first argument clamped between the other two arguments. + +**Example:** +```tomo +>> 2.5:clamped(5.5, 10.5) += 5.5 +``` diff --git a/docs/pointers.md b/docs/pointers.md new file mode 100644 index 00000000..ee7a5d8f --- /dev/null +++ b/docs/pointers.md @@ -0,0 +1,133 @@ +# Pointers + +Pointers are numeric values that represent a location in memory where some type +of data lives. Pointers are created using either the `@` prefix operator to +**a**llocate heap memory or the `&` prefix operator to get the address of a +variable. Stack pointers (`&`) are more limited than heap pointers (`@`) and +cannot be stored inside an array, set, table, struct, enum, or channel. +However, stack pointers are useful for methods that mutate local variables and +don't need to save the pointer anywhere. + +Pointers are the way in Tomo that you can create mutable data. All +datastructures are by default, immutable, but using pointers, you can create +a region of memory where different immutable values can be held, which change +over time. Essentially, you can think about mutation as the act of creating +a new, different value and assigning it to a pointer's memory location to +replace the value that previously resided there. + +```tomo +func no_mutation_possible(nums:[Int]): + nums[1] = 10 // This performs a copy-on-write and creates a new array + // The new array is only accessible as a local variable here +... +my_nums := [0, 1, 2] +no_mutation_possible(my_nums) +>> my_nums += [0, 1, 2] + +func do_mutation(nums:@[Int]): + nums[1] = 10 // The mutates the value at the given pointer's location +... +my_nums := @[0, 1, 2] +do_mutation(my_nums) +>> my_nums += @[10, 1, 2] +``` + +In general, heap pointers can be used as stack pointers if necessary, since +the usage of stack pointers is restricted, but heap pointers don't have the +same restrictions, so it's good practice to define functions that don't need +to store pointers to use stack references. This lets you pass references to +local variables or pointers to heap data depending on your needs. + +```tomo +func swap_first_two(data:&[Int]): + data[1], data[2] = data[2], data[1] + +... + +heap_nums := @[10, 20, 30] +swap_first_two(heap_nums) + +local_nums := [10, 20, 30] +swap_first_two(&local_nums) +``` + +## Dereferencing + +Pointers can be dereferenced to access the value that's stored at the pointer's +memory location using the `[]` postfix operator (with no value inside). + +```tomo +nums := @[10, 20] +>> nums[] += [10, 20] +``` + +## Equality and Comparisons + +When comparing two pointers, the comparison operates on the _memory address_, +not the contents of the memory. This is "referential" equality, not +"structural" equality. The easy way to think about it is that two pointers are +equal to each other only if doing a mutation to one of them is the same as +doing a mutation to the other. + +```tomo +x := @[10, 20, 30] +y := @[10, 20, 30] +>> x == y += no + +z := x +>> x == z += yes +``` + +Pointers are ordered by memory address, which is somewhat arbitrary, but +consistent. + +## Null Safety + +Tomo pointers are, by default, guaranteed to be non-null. If you write a +function that takes either a `&T` or `@T`, the value that will be given +is always non-null. However, optional pointers can be used by adding a +question mark to the type: `&T?` or `@T?`. A null value can be created +using the syntax `!@T` or `!&T`. You can also append a question mark to +a pointer value so the type checker knows it's supposed to be optional: + +``` +optional := @[10, 20]? +optional := &foo? +``` + +The compiler will not allow you to dereference an optionally null pointer +without explicitly checking for null. To do so, use pattern matching like +this: + +``` +when optional is @ptr: + ok := ptr[] +else: + say("Oh, it was null") +``` + +## Using Pointers + +For convenience, most operations that work on values can work with pointers to +values implicitly. For example, if you have a struct type with a `.foo` field, +you can use `ptr.foo` on a pointer to that struct type as well, without needing +to use `ptr[].foo`. The same is true for array accesses like `ptr[i]` and method +calls like `ptr:reversed()`. + +As a matter of convenience, local variables can also be automatically promoted +to stack references when invoking methods that require a stack reference as the +first argument. For example: + +```tomo +func swap_first_two(arr:&[Int]): + arr[1], arr[2] = arr[2], arr[1] +... +my_arr := [10, 20, 30] // not a pointer +swap_first_two(my_arr) // ok, automatically converted to &my_arr +my_arr:shuffle() // ok, automatically converted to &my_arr +``` diff --git a/docs/ranges.md b/docs/ranges.md new file mode 100644 index 00000000..4771bd58 --- /dev/null +++ b/docs/ranges.md @@ -0,0 +1,65 @@ +# Ranges + +Ranges are Tomo's way to do iteration over numeric ranges. Ranges are typically +created using the `Int.to()` method like so: `5:to(10)`. Ranges are +*inclusive*. + +```tomo +>> [i for i in 3:to(5)] += [3, 4, 5] +``` + +--- + +## Range Methods + +### `reversed` + +**Description:** +Returns a reversed copy of the range. + +**Usage:** +```tomo +reversed(range: Range) -> Range +``` + +**Parameters:** + +- `range`: The range to be reversed. + +**Returns:** +A new `Range` with the order of elements reversed. + +**Example:** +```tomo +>> 1:to(5):reversed() += Range(first=5, last=1, step=-1) +``` + +--- + +### `by` + +**Description:** +Creates a new range with a specified step value. + +**Usage:** +```tomo +by(range: Range, step: Int) -> Range +``` + +**Parameters:** + +- `range`: The original range. +- `step`: The step value to be used in the new range. + +**Returns:** +A new `Range` that increments by the specified step value. + +**Example:** +```tomo +>> 1:to(5):by(2) += Range(first=1, last=5, step=2) +``` + +--- diff --git a/docs/sets.md b/docs/sets.md new file mode 100644 index 00000000..850d1443 --- /dev/null +++ b/docs/sets.md @@ -0,0 +1,356 @@ +# Sets + +Sets represent an unordered collection of unique elements. These are +implemented using hash tables. + +```tomo +a := {10, 20, 30} +b := {20, 30} +>> a:overlap(b) += {20} +``` + +## Syntax + +Sets are written using `{}` curly braces with comma-separated items: + +```tomo +nums := {10, 20, 30} +``` + +Empty sets must specify the item type explicitly: + +```tomo +empty := {:Int} +``` + +For type annotations, a set that holds items with type `T` is written as `{T}`. + +### Comprehensions + +Similar to arrays, sets can use comprehensions: + +```tomo +set := {10*i for i in 10} +set2 := {10*i for i in 10 if i mod 2 == 0} +set3 := {-10, 10*i for i in 10} +``` + +## Accessing Items + +Sets internally store their items in an array, which you can access with the +`.items` field. This is a constant-time operation that produces an immutable +view: + +```tomo +set := {10, 20, 30} +>> set.items += [10, 20, 30] +``` + +## Length + +Set length can be accessed by the `.length` field: + +```tomo +>> {10, 20, 30}.length += 3 +``` + +## Iteration + +You can iterate over the items in a table like this: + +```tomo +for item in set: + ... + +for i, item in set: + ... +``` + +Set iteration operates over the value of the set when the loop began, so +modifying the set during iteration is safe and will not result in the loop +iterating over any of the new values. + +## Set Methods + +### `has` + +**Description:** +Checks if the set contains a specified item. + +**Usage:** +```tomo +has(set:{T}, item:T) -> Bool +``` + +**Parameters:** + +- `set`: The set to check. +- `item`: The item to check for presence. + +**Returns:** +`yes` if the item is present, `no` otherwise. + +**Example:** +```tomo +>> {10, 20}:has(20) += yes +``` + +--- + +### `add` + +**Description:** +Adds an item to the set. + +**Usage:** +```tomo +add(set:{T}, item: T) -> Void +``` + +**Parameters:** + +- `set`: The mutable reference to the set. +- `item`: The item to add to the set. + +**Returns:** +Nothing. + +**Example:** +```tomo +>> nums:add(42) +``` + +--- + +### `add_all` + +**Description:** +Adds multiple items to the set. + +**Usage:** +```tomo +add_all(set:&{T}, items: [T]) -> Void +``` + +**Parameters:** + +- `set`: The mutable reference to the set. +- `items`: The array of items to add to the set. + +**Returns:** +Nothing. + +**Example:** +```tomo +>> nums:add_all([1, 2, 3]) +``` + +--- + +### `remove` + +**Description:** +Removes an item from the set. + +**Usage:** +```tomo +remove(set:&{T}, item: T) -> Void +``` + +**Parameters:** + +- `set`: The mutable reference to the set. +- `item`: The item to remove from the set. + +**Returns:** +Nothing. + +**Example:** +```tomo +>> nums:remove(42) +``` + +--- + +### `remove_all` + +**Description:** +Removes multiple items from the set. + +**Usage:** +```tomo +remove_all(set:&{T}, items: [T]) -> Void +``` + +**Parameters:** + +- `set`: The mutable reference to the set. +- `items`: The array of items to remove from the set. + +**Returns:** +Nothing. + +**Example:** +```tomo +>> nums:remove_all([1, 2, 3]) +``` + +--- + +### `clear` + +**Description:** +Removes all items from the set. + +**Usage:** +```tomo +clear(set:&{T}) -> Void +``` + +**Parameters:** + +- `set`: The mutable reference to the set. + +**Returns:** +Nothing. + +**Example:** +```tomo +>> nums:clear() +``` + +--- + +### `with` + +**Description:** +Creates a new set that is the union of the original set and another set. + +**Usage:** +```tomo +with(set:{T}, other: {T}) -> {T} +``` + +**Parameters:** + +- `set`: The original set. +- `other`: The set to union with. + +**Returns:** +A new set containing all items from both sets. + +**Example:** +```tomo +>> {1, 2}:with({2, 3}) += {1, 2, 3} +``` + +--- + +### `overlap` + +**Description:** +Creates a new set with items that are in both the original set and another set. + +**Usage:** +```tomo +overlap(set:{T}, other: {T}) -> {T} +``` + +**Parameters:** + +- `set`: The original set. +- `other`: The set to intersect with. + +**Returns:** +A new set containing only items present in both sets. + +**Example:** +```tomo +>> {1, 2}:overlap({2, 3}) += {2} +``` + +--- + +### `without` + +**Description:** +Creates a new set with items from the original set but without items from another set. + +**Usage:** +```tomo +without(set:{T}, other: {T}) -> {T} +``` + +**Parameters:** + +- `set`: The original set. +- `other`: The set of items to remove from the original set. + +**Returns:** +A new set containing items from the original set excluding those in the other set. + +**Example:** +```tomo +>> {1, 2}:without({2, 3}) += {1} +``` + +--- + +### `is_subset_of` + +**Description:** +Checks if the set is a subset of another set. + +**Usage:** +```tomo +set:is_subset_of(other: {T}, strict: Bool = no) -> Bool +``` + +**Parameters:** + +- `set`: The set to check. +- `other`: The set to compare against. +- `strict`: If `yes`, checks if the set is a strict subset (does not equal the other set). + +**Returns:** +`yes` if the set is a subset of the other set (strictly or not), `no` otherwise. + +**Example:** +```tomo +>> {1, 2}:is_subset_of({1, 2, 3}) += yes +``` + +--- + +### `is_superset_of` + +**Description:** +Checks if the set is a superset of another set. + +**Usage:** +```tomo +is_superset_of(set:{T}, other: {T}, strict: Bool = no) -> Bool +``` + +**Parameters:** + +- `set`: The set to check. +- `other`: The set to compare against. +- `strict`: If `yes`, checks if the set is a strict superset (does not equal the other set). + +**Returns:** +`yes` if the set is a superset of the other set (strictly or not), `no` otherwise. + +**Example:** +```tomo +>> {1, 2, 3}:is_superset_of({1, 2}) += yes +``` diff --git a/docs/structs.md b/docs/structs.md new file mode 100644 index 00000000..4ab78fed --- /dev/null +++ b/docs/structs.md @@ -0,0 +1,39 @@ +# Structs + +In Tomo, you can define your own structs, which hold members with arbitrary +types that can be accessed by fields: + +```tomo +struct Foo(name:Text, age:Int) +... +>> my_foo := Foo("Bob", age=10) += Foo(name="Bob", age=10) +>> my_foo.name += "Bob" +``` + +Structs are value types and comparisons on them operate on the member values +one after the other. + +## Namespaces + +Structs can define their own methods that can be called with a `:` or different +values that are stored on the type itself. + +```tomo +struct Foo(name:Text, age:Int): + oldest := Foo("Methuselah", 969) + + func greet(f:Foo): + say("Hi my name is $f.name and I am $f.age years old!") + + func get_older(f:&Foo): + f.age += 1 +... +my_foo := Foo("Alice", 28) +my_foo:greet() +my_foo:get_older() +``` + +Method calls work when the first argument is the struct type or a pointer to +the struct type. diff --git a/docs/tables.md b/docs/tables.md new file mode 100644 index 00000000..f4753efb --- /dev/null +++ b/docs/tables.md @@ -0,0 +1,294 @@ +# Tables + +Tables are Tomo's associative mapping structure, also known as a Dictionary or +Map. Tables are efficiently implemented as a hash table that preserves +insertion order and has fast access to keys and values as array slices. Tables +support *all* types as both keys and values. + +Tables do not support square bracket indexing (`t[key]`), but instead rely on +the methods `:get(key)` and `:set(key, value)`. This is explicit to avoid +hiding the fact that table lookups and table insertion are performing function +calls and have edge conditions like a failure to find an entry. + +## Syntax + +Tables are written using `{}` curly braces with `:` colons associating key +expressions with value expressions and commas between entries: + +```tomo +table := {"A": 10, "B": 20} +``` + +Empty tables must specify the key and value types explicitly: + +```tomo +empty := {:Text:Int} +``` + +For type annotations, a table that maps keys with type `K` to values of type +`V` is written as `{K:V}`. + +### Comprehensions + +Similar to arrays, tables can use comprehensions to dynamically construct tables: + +```tomo +t := {i: 10*i for i in 10} +t := {i: 10*i for i in 10 if i mod 2 == 0} +t := {-1:-10, i: 10*i for i in 10} +``` + +### Fallback Tables + +Tables can specify a fallback table that is used when looking up a value if it +is not found in the table itself: + +```tomo +t := {"A": 10} +t2 := {"B": 20; fallback=t} +>> t2:get("A") += 10 +``` + +The fallback is available by the `.fallback` field, which returns an optional +readonly pointer to the fallback table (if present) or null if it is not. + +## Length + +Table length can be accessed by the `.length` field: + +```tomo +>> {"A":10, "B":20}.length += 2 +``` + +## Accessing Keys and Values + +The keys and values of a table can be efficiently accessed as arrays using a +constant-time immutable slice of the internal data from the table: + +```tomo +t := {"A": 10, "B": 20} +>> t.keys += ["A", "B"] +>> t.values += [10, 20] +``` + +## Iteration + +You can iterate over the key/value pairs in a table like this: + +```tomo +for key, value in table: + ... + +for key in table: + ... +``` + +Table iteration operates over the value of the table when the loop began, so +modifying the table during iteration is safe and will not result in the loop +iterating over any of the new values. + +## Table Methods + +### `bump` + +**Description:** +Increments the value associated with a key by a specified amount. If the key is +not already in the table, its value will be assumed to be zero. + +**Usage:** +```markdown +bump(t:{K:V}, key: K, amount: Int = 1) -> Void +``` + +**Parameters:** + +- `t`: The mutable reference to the table. +- `key`: The key whose value is to be incremented. +- `amount`: The amount to increment the value by (default: 1). + +**Returns:** +Nothing. + +**Example:** +```markdown +>> t := {"A":1} +t:bump("A") +t:bump("B", 10) +>> t += {"A": 2, "B": 10} +``` + +--- + +### `clear` + +**Description:** +Removes all key-value pairs from the table. + +**Usage:** +```markdown +t:clear() -> Void +``` + +**Parameters:** + +- `t`: The mutable reference to the table. + +**Returns:** +Nothing. + +**Example:** +```markdown +>> t:clear() +``` + +--- + +### `get` + +**Description:** +Retrieves the value associated with a key, or returns a default value if the key is not present. + +**Usage:** +```markdown +t:get(key: K, default: V) -> V +``` + +**Parameters:** + +- `t`: The table. +- `key`: The key whose associated value is to be retrieved. +- `default`: The value to return if the key is not present. If this argument is + not provided, a runtime error will be created if the key is not present. + +**Returns:** +The value associated with the key or the default value if the key is not found. + +**Example:** +```markdown +>> t := {"A":1, "B":2} +>> t:get("A") += 1 + +>> t:get("xxx", 0) += 0 +``` + +--- + +### `get_or_null` + +**Description:** +Retrieves the value associated with a key, or returns `null` if the key is not present. +This method is only available on tables whose values are pointers. + +**Usage:** +```markdown +t:get_or_null(key: K) -> @V? +``` + +**Parameters:** + +- `t`: The table. +- `key`: The key whose associated value is to be retrieved. + +**Returns:** +A mutable reference to the value associated with the key or `null` if the key is not found. + +**Example:** +```markdown +>> t := {"A": @[10]} +>> t:get_or_null("A") += @[10]? +>> t:get_or_null("xxx") += !@[Int] +``` + +--- + +### `has` + +**Description:** +Checks if the table contains a specified key. + +**Usage:** +```markdown +has(t:{K:V}, key: K) -> Bool +``` + +**Parameters:** + +- `t`: The table. +- `key`: The key to check for presence. + +**Returns:** +`yes` if the key is present, `no` otherwise. + +**Example:** +```markdown +>> {"A":1, "B":2}:has("A") += yes +>> {"A":1, "B":2}:has("xxx") += no +``` + +--- + +### `remove` + +**Description:** +Removes the key-value pair associated with a specified key. + +**Usage:** +```markdown +remove(t:{K:V}, key: K) -> Void +``` + +**Parameters:** + +- `t`: The mutable reference to the table. +- `key`: The key of the key-value pair to remove. + +**Returns:** +Nothing. + +**Example:** +```markdown +t := {"A":1, "B":2} +t:remove("A") +>> t += {"B": 2} +``` + +--- + +### `set` + +**Description:** +Sets or updates the value associated with a specified key. + +**Usage:** +```markdown +set(t:{K:V}, key: K, value: V) -> Void +``` + +**Parameters:** + +- `t`: The mutable reference to the table. +- `key`: The key to set or update. +- `value`: The value to associate with the key. + +**Returns:** +Nothing. + +**Example:** +```markdown +t := {"A": 1, "B": 2} +t:set("C", 3) +>> t += {"A": 1, "B": 2, "C": 3} +``` diff --git a/docs/text.md b/docs/text.md new file mode 100644 index 00000000..70708fe4 --- /dev/null +++ b/docs/text.md @@ -0,0 +1,778 @@ +# Text + +`Text` is Tomo's datatype to represent text. The name `Text` is used instead of +"string" because Tomo represents text as an immutable UTF-8-encoded value that +uses the Boehm Cord library for efficient storage and concatenation. These are +_not_ C-style NULL-terminated character arrays. GNU libunistring is used for +full Unicode functionality (grapheme cluster counts, capitalization, etc.). + +## Syntax + +Text has a flexible syntax designed to make it easy to hold values from +different languages without the need to have lots of escape sequences and +without using printf-style string formatting. + +``` +// Basic text: +str := "Hello world" +str2 := 'Also text' +str3 := `Backticks too` +``` + +## Line Splits + +Long text can be split across multiple lines by having two or more dots at the +start of a new line on the same indentation level that started the text: + +``` +str := "This is a long +....... line that is split in code" +``` + +## Multi-line Text + +Multi-line text has indented (i.e. at least one tab more than the start of the +text) text inside quotation marks. The leading and trailing newline are +ignored: + +``` +multi_line := " + This text has multiple lines. + Line two. + + You can split a line +.... using two or more dots to make an elipsis. + + Remember to include whitespace after the elipsis if desired. + + Or don't if you're splitting a long word like supercalifragilisticexpia +....lidocious + + This text is indented by one level in the text + + "quotes" are ignored unless they're at the same indentation level as the +.... start of the text. + + The end (no newline after this). +" +``` + +## Text Interpolations + +Inside double quoted text, you can use a dollar sign (`$`) to insert an +expression that you want converted to text. This is called text interpolation: + +``` +// Interpolation: +my_var := 5 +str := "My var is $my_var!" +// Equivalent to "My var is 5!" + +// Using parentheses: +str := "Sum: $(1 + 2)" +// equivalent to "Sum: 3" +``` + +Single-quoted text does not have interpolations: + +``` +// No interpolation here: +str := 'Sum: $(1 + 2)' +``` + +## Text Escapes + +Unlike other languages, backslash is *not* a special character inside of text. +For example, `"x\ny"` has the characters `x`, `\`, `n`, `y`, not a newline. +Instead, a series of character escapes act as complete text literals without +quotation marks: + +``` +newline := \n +crlf := \r\n +quote := \" +``` + +These text literals can be used as interpolation values with or without +parentheses, depending on which you find more readable: + +``` +two_lines := "one$(\n)two" +has_quotes := "some $\"quotes$\" here" +``` + +However, in general it is best practice to use multi-line text to avoid these problems: + +``` +str := " + This has + multiple lines and "quotes" too! +" +``` + +### Multi-line Text + +There are two reasons for text to span multiple lines in code: either you +have text that contains newlines and you want to represent it without `\n` +escapes, or you have a long single-line text that you want to split across +multiple lines for readability. To support this, you can use newlines inside of +text with indentation-sensitivity. For splitting long lines, use two or more +"."s at the same indentation level as the start of the text literal: + +``` +single_line := "This is a long text that +... spans multiple lines" +``` +For text that contains newlines, you may put multiple indented lines inside +the quotes: + +``` +multi_line := " + line one + line two + this line is indented + last line +" +``` + +Text may only end on lines with the same indentation as the starting quote +and nested quotes are ignored: + +``` +multi_line := " + Quotes in indented regions like this: " don't count +" +``` + +If there is a leading or trailing newline, it is ignored and not included in +the text. + +``` +str := " + one line +" + +>>> str == "one line" +=== yes +``` + +Additional newlines *are* counted though: + +``` +str := " + + blank lines + +" + +>>> str == "{\n}blank lines{\n}" +``` + +### Customizable `$`-Text + +Sometimes you might need to use a lot of literal `$`s or quotation marks in +text. In such cases, you can use the more customizable form of text. The +customizable form lets you explicitly specify which character to use for +interpolation and which characters to use for delimiting the text. + +The first character after the `$` is the custom interpolation character, which +can be any of the following symbols: `~!@#$%^&*+=\?`. If none of these +characters is used, the default interpolation character is `$`. Since this is +the default, you can disable interpolation altogether by using `$` here (i.e. a +double `$$`). + +The next thing in a customizable text is the character used to delimit the +text. The text delimiter can be any of the following symbols: `` "'`|/;([{< `` +If the text delimiter is one of `([{<`, then the text will continue until a +matching `)]}>` is found, not terminating unless the delimiters are balanced +(i.e. nested pairs of delimiters are considered part of the text). + +Here are some examples: + +``` +$"Equivalent to normal text with dollar interps: $(1 + 2)" +$@"The same, but the AT symbol interpolates: @(1 + 2)" +$$"No interpolation here, $ is just a literal character" +$|This text is pipe-delimited, so it can have "quotes" and 'single quotes' and interpolates with dollar sign: $(1+2)| +$(This text is parens-delimited, so you can have (nested) parens without ending the text) +$=[This text is square-bracket delimited [which can be nested] and uses equals for interps: =(1 + 2)] +$@/look ma, regex literals!/ +``` + +When text is delimited by matching pairs (`()`, `[]`, `{}`, or `<>`), they +can only be closed by a matched closing character at the same indentation +level, ignoring nested pairs: + +``` +$$(Inside parens, you can have (nested ()) parens no problem) +$$"But only (), [], {}, and <> are matching pairs, you can't have nested quotes" +$$( + When indented, an unmatched ) won't close the text + An unmatched ( won't mess things up either + Only matching pairs on the same indentation level are counted: +) +$$(Multi-line text with nested (parens) and +.. line continuation) +``` + +As a special case, when you use the same character for interpolation and text +delimiting, no interpolations are allowed: + +``` +plain := $""This text has {no interpolations}!" +``` + +**Note:** Normal doubly quoted text with no dollar sign (e.g. `"foo"`) are a +shorthand for `${}"foo"`. Singly quoted text with no dollar sign (e.g. +`'foo'`) are shorthand for `$''foo'`. + +## Operations + +### Concatenation + +Concatenation in the typical case is an O(1) operation: `"{x}{y}"` or `x ++ y`. + +Because text concatenation is typically an O(1) operation, there is no need for +a separate "string builder" class in the language and no need to use an array +of text fragments. + +### Text Length + +Text length is an ambiguous term in the context of UTF-8 text. There are +several possible meanings, so each of these meanings is split into a separate +method: + +- Number of grapheme clusters: `text:num_clusters()`. This is probably what + you want to use, since it corresponds to the everyday notion of "letters". +- Size in bytes: `text:num_bytes()` +- Number of unicode codepoints: `text:num_codepoints()` (you probably want to + use clusters, not codepoints in most applications) + +Since the typical user expectation is that text length refers to "letters," +the `#` length operator returns the number of grapheme clusters, which is the +closest unicode equivalent to "letters." + +### Iteration + +Iteration is *not* supported for text because of the ambiguity between bytes, +codepoints, and grapheme clusters. It is instead recommended that you +explicitly iterate over bytes, codepoints, graphemes, words, lines, etc: + +### Equality, Comparison, and Hashing + +All text is compared and hashed using unicode normalization. Unicode provides +several different ways to represent the same text. For example, the single +codepoint `U+E9` (latin small e with accent) is rendered the same as the two +code points `U+65 U+301` (latin small e, acute combining accent) and has an +equivalent linguistic meaning. These are simply different ways to represent the +same "letter." In order to make it easy to write correct code that takes this +into account, Tomo uses unicode normalization for all text comparisons and +hashing. Normalization does the equivalent of converting text to a canonical +form before performing comparisons or hashing. This means that if a table is +created that has text with the codepoint `U+E9` as a key, then a lookup with +the same text but with `U+65 U+301` instead of `U+E9` will still succeed in +finding the value because the two texts are equivalent under normalization. + + +# Text Functions + +## `as_c_string` + +**Description:** +Converts a `Text` value to a C-style string. + +**Usage:** +```tomo +as_c_string(text: Text) -> CString +``` + +**Parameters:** + +- `text`: The text to be converted to a C-style string. + +**Returns:** +A C-style string (`CString`) representing the text. + +**Example:** +```tomo +>> "Hello":as_c_string() += CString("Hello") +``` + +--- + +## `bytes` + +**Description:** +Converts a `Text` value to an array of bytes. + +**Usage:** +```tomo +bytes(text: Text) -> [Int8] +``` + +**Parameters:** + +- `text`: The text to be converted to bytes. + +**Returns:** +An array of bytes (`[Int8]`) representing the text. + +**Example:** +```tomo +>> "Amélie":bytes() += [65_i8, 109_i8, 101_i8, -52_i8, -127_i8, 108_i8, 105_i8, 101_i8] +``` + +--- + +## `character_names` + +**Description:** +Returns a list of character names from the text. + +**Usage:** +```tomo +character_names(text: Text) -> [Text] +``` + +**Parameters:** + +- `text`: The text from which to extract character names. + +**Returns:** +A list of character names (`[Text]`). + +**Example:** +```tomo +>> "Amélie":character_names() += ["LATIN CAPITAL LETTER A", "LATIN SMALL LETTER M", "LATIN SMALL LETTER E", "COMBINING ACUTE ACCENT", "LATIN SMALL LETTER L", "LATIN SMALL LETTER I", "LATIN SMALL LETTER E"] +``` + +--- + +## `clusters` + +**Description:** +Breaks the text into a list of unicode graphical clusters. Clusters are what +you typically think of when you think of "letters" or "characters". If you're +in a text editor and you hit the left or right arrow key, it will move the +cursor by one graphical cluster. + +**Usage:** +```tomo +clusters(text: Text) -> [Text] +``` + +**Parameters:** + +- `text`: The text to be broken into graphical clusters. + +**Returns:** +A list of graphical clusters (`[Text]`) within the text. + +**Example:** +```tomo +>> "Amélie":clusters() += ["A", "m", "é", "l", "i", "e"] : [Text] +``` + +--- + +## `codepoints` + +**Description:** +Returns a list of Unicode code points for the text. + +**Usage:** +```tomo +codepoints(text: Text) -> [Int32] +``` + +**Parameters:** + +- `text`: The text from which to extract Unicode code points. + +**Returns:** +A list of Unicode code points (`[Int32]`). + +**Example:** +```tomo +>> "Amélie":codepoints() += [65_i32, 109_i32, 101_i32, 769_i32, 108_i32, 105_i32, 101_i32] : [Int32] +``` + +--- + +## `from_c_string` + +**Description:** +Converts a C-style string to a `Text` value. + +**Usage:** +```tomo +from_c_string(str: CString) -> Text +``` + +**Parameters:** + +- `str`: The C-style string to be converted. + +**Returns:** +A `Text` value representing the C-style string. + +**Example:** +```tomo +>> Text.from_c_string(CString("Hello")) += "Hello" +``` + +--- + +## `has` + +**Description:** +Checks if the `Text` contains a target substring. + +**Usage:** +```tomo +has(text: Text, target: Text, where: Where = Where.Anywhere) -> Bool +``` + +**Parameters:** + +- `text`: The text to be searched. +- `target`: The substring to search for. +- `where`: The location to search (`Where.Anywhere` by default). This can + also be `Start` or `End`. + +**Returns:** +`yes` if the target substring is found, `no` otherwise. + +**Example:** +```tomo +>> "hello world":has("wo") += yes +>> "hello world":has("wo", where=Start) += no +>> "hello world":has("he", where=Start) += yes +``` + +--- + +## `join` + +**Description:** +Joins a list of text pieces with a specified glue. + +**Usage:** +```tomo +join(glue: Text, pieces: [Text]) -> Text +``` + +**Parameters:** + +- `glue`: The text used to join the pieces. +- `pieces`: The list of text pieces to be joined. + +**Returns:** +A single `Text` value with the pieces joined by the glue. + +**Example:** +```tomo +>> ", ":join(["one", "two", "three"]) += "one, two, three" +``` + +--- + +## `lower` + +**Description:** +Converts all characters in the text to lowercase. + +**Usage:** +```tomo +lower(text: Text) -> Text +``` + +**Parameters:** + +- `text`: The text to be converted to lowercase. + +**Returns:** +The lowercase version of the text. + +**Example:** +```tomo +>> "AMÉLIE":lower() += "amélie" +``` + +--- + +## `num_bytes` + +**Description:** +Returns the number of bytes used by the text. + +**Usage:** +```tomo +num_bytes(text: Text) -> Int +``` + +**Parameters:** + +- `text`: The text to measure. + +**Returns:** +The number of bytes used by the text. + +**Example:** +```tomo +>> "Amélie":num_bytes() += 8 +``` + +--- + +## `num_clusters` + +**Description:** +Returns the number of clusters in the text. + +**Usage:** +```tomo +num_clusters(text: Text) -> Int +``` + +**Parameters:** + +- `text`: The text to measure. + +**Returns:** +The number of clusters in the text. + +**Example:** +```tomo +>> "Amélie":num_clusters() += 6 +``` + +--- + +## `num_codepoints` + +**Description:** +Returns the number of Unicode code points in the text. + +**Usage:** +```tomo +num_codepoints(text: Text) -> Int +``` + +**Parameters:** + +- `text`: The text to measure. + +**Returns:** +The number of Unicode code points in the text. + +**Example:** +```tomo +>> "Amélie":num_codepoints() += 7 +``` + +--- + +## `quoted` + +**Description:** +Formats the text as a quoted string. + +**Usage:** +```tomo +quoted(text: Text, color: Bool = no) -> Text +``` + +**Parameters:** + +- `text`: The text to be quoted. +- `color`: Whether to add color formatting (default is `no`). + +**Returns:** +The text formatted as a quoted string. + +**Example:** +```tomo +>> "one$(\n)two":quoted() += "\"one\\ntwo\"" +``` + +--- + +## `replace` + +**Description:** +Replaces occurrences of a pattern in the text with a replacement string. + +**Usage:** +```tomo +replace(text: Text, pattern: Text, replacement: Text, limit: Int = -1) -> Text +``` + +**Parameters:** + +- `text`: The text in which to perform replacements. +- `pattern`: The substring to be replaced. +- `replacement`: The text to replace the pattern with. +- `limit`: The maximum number of replacements (default is `-1`, meaning no limit). + +**Returns:** +The text with occurrences of the pattern replaced. + +**Example:** +```tomo +>> "Hello world":replace("world", "there") += "Hello there" + +>> "xxxx":replace("x", "y", limit=2) += "yyxx" +``` + +--- + +## `split` + +**Description:** +Splits the text into a list of substrings based on a delimiter. + +**Usage:** +```tomo +split(text: Text, split: Text) -> [Text] +``` + +**Parameters:** + +- `text`: The text to be split. +- `split`: The delimiter used to split the text. + +**Returns:** +A list of substrings resulting from the split. + +**Example:** +```tomo +>> "one,two,three":split(",") += ["one", "two", "three"] +``` + +--- + +## `title` + +**Description:** +Converts the text to title case (capitalizing the first letter of each word). + +**Usage:** +```tomo +title(text: Text) -> Text +``` + +**Parameters:** + +- `text`: The text to be converted to title case. + +**Returns:** +The text in title case. + +**Example:** +```tomo +>> "amélie":title() += "Amélie" +``` + +--- + +## `trimmed` + +**Description:** +Trims characters from the beginning and end of the text. + +**Usage:** +```tomo +trimmed(text: Text, trim: Text = " {\n\r\t}", where: Where = Where.Anywhere) -> Text +``` + +**Parameters:** + +- `text`: The text to be trimmed. +- `trim`: The set of characters to remove (default is `" {\n\r\t}"`). +- `where`: Specifies where to trim (`Where.Anywhere` by default). + +**Returns:** +The trimmed text. + +**Example:** +```tomo +>> " xxx ":trimmed() += "xxx" + +>> "xxyyxx":trimmed("x", where=Start) += "yyxx" +``` + +--- + +## `upper` + +**Description:** +Converts all characters in the text to uppercase. + +**Usage:** +```tomo +upper(text: Text) -> Text +``` + +**Parameters:** + +- `text`: The text to be converted to uppercase. + +**Returns:** +The uppercase version of the text. + +**Example:** +```tomo +>> "amélie":upper() += "AMÉLIE" +``` + +--- + +## `without` + +**Description:** +Removes all occurrences of a target substring from the text. + +**Usage:** +```tomo +without(text: Text, target: Text, where: Where = Where.Anywhere) -> Text +``` + +**Parameters:** + +- `text`: The text from which to remove substrings. +- `target`: The substring to remove. +- `where`: The location to remove the target (`Where.Anywhere` by default). + +**Returns:** +The text with occurrences of the target removed. + +**Example:** +```tomo +>> "banana":without("na") += "ba" +>> "banana":without("na", where=End) += "bana" +``` diff --git a/docs/threads.md b/docs/threads.md new file mode 100644 index 00000000..228fc8ac --- /dev/null +++ b/docs/threads.md @@ -0,0 +1,111 @@ +# Threads + +Tomo supports POSIX threads (pthreads) through the `Thread` type. The +recommended practice is to have each thread interact with other threads only +through thread-safe Channels with no other shared data. + +## Thread Methods + +### `new` + +**Description:** +Creates a new thread to execute a specified function. + +**Usage:** +```tomo +Thread.new(fn: func() -> Void) -> Thread +``` + +**Parameters:** + +- `fn`: The function to be executed by the new thread. + +**Returns:** +A new `Thread` object representing the created thread. + +**Example:** +```tomo +>> jobs := |Int| +>> results := |Int| +>> thread := Thread.new(func(): + while yes: + input := jobs:get() + results:give(input + 10 +) += Thread<0x12345678> +>> jobs:give(10) +>> results:get() += 11 +``` + +--- + +### `cancel` + +**Description:** +Requests the cancellation of a specified thread. + +**Usage:** +```tomo +cancel(thread: Thread) -> Void +``` + +**Parameters:** + +- `thread`: The thread to cancel. + +**Returns:** +Nothing. + +**Example:** +```tomo +>> thread:cancel() +``` + +--- + +### `join` + +**Description:** +Waits for a specified thread to terminate. + +**Usage:** +```tomo +join(thread: Thread) -> Void +``` + +**Parameters:** + +- `thread`: The thread to join. + +**Returns:** +Nothing. + +**Example:** +```tomo +>> thread:join() +``` + +--- + +### `detach` + +**Description:** +Detaches a specified thread, allowing it to run independently. + +**Usage:** +```tomo +detach(thread: Thread) -> Void +``` + +**Parameters:** + +- `thread`: The thread to detach. + +**Returns:** +Nothing. + +**Example:** +```tomo +>> thread:detach() +``` -- cgit v1.2.3