diff options
67 files changed, 2518 insertions, 2517 deletions
@@ -46,7 +46,7 @@ of many language features or the other example programs/modules in - Memory safety (garbage collection, [compiler-enforced null safety](docs/pointers.md), [automatic array bounds - checking](docs/arrays.md), and no uninitialized variables) + checking](docs/lists.md), and no uninitialized variables) - High-performance [arbitrary-precision integers](docs/integers.md) by default with opt-in fixed-size integers with arithmetic overflow checking - [Type-safe strings representing different languages](docs/langs.md) with @@ -55,7 +55,7 @@ of many language features or the other example programs/modules in unions/enums)](docs/enums.md) - Type-safe [optional values](docs/optionals.tm) with low syntax overhead - Efficient datastructures with immutable value semantics: - [arrays](docs/arrays.md), [tables](docs/tables.md), [sets](docs/sets.md), + [lists](docs/lists.md), [tables](docs/tables.md), [sets](docs/sets.md), [text](docs/text.md). - [Privacy-protecting types](docs/structs.md#Secret-Values) that help prevent accidentally logging sensitive information @@ -70,7 +70,7 @@ of many language features or the other example programs/modules in - [String interpolation](docs/text.md) and debug printing builtins - Built-in datastructures with a rich library of commonly used methods: - [arrays](docs/arrays.md), [tables](docs/tables.md), [sets](docs/sets.md), + [lists](docs/lists.md), [tables](docs/tables.md), [sets](docs/sets.md), [text](docs/text.md). - Full-featured [libraries/modules](docs/libraries.md) - [Full UTF8 support](docs/text.md) for all text operations diff --git a/docs/README.md b/docs/README.md index 05d335cc..e8cd4a2f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -18,7 +18,7 @@ A few topics that are documented: Information about Tomo's built-in types can be found here: -- [Arrays](arrays.md) +- [Lists](lists.md) - [Booleans](booleans.md) - [Bytes](bytes.md) - [Enums](enums.md) diff --git a/docs/arrays.md b/docs/arrays.md deleted file mode 100644 index ec114442..00000000 --- a/docs/arrays.md +++ /dev/null @@ -1,908 +0,0 @@ -# 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] -``` - -## Implementation Details - -Under the hood, arrays are implemented as a struct that contains a pointer to a -contiguous chunk of memory storing the elements of the array and some other -metadata. Since Tomo has datatypes with different sizes, like `Bool`s which -take one byte and `struct`s which can take up many bytes, it's worth noting -that arrays store the elements compactly and inline, without the need for each -array cell to hold a pointer to where the data actually lives. - -The other metadata stored with an array includes its length as well as the -_stride_ of the array. The stride is not exposed to the user, but it's the gap -in bytes between each element in the array. The reason this is mentioned is -that it is possible to create immutable slices of arrays in constant time by -creating a new struct that points to the appropriate starting place for the -array items and has the appropriate stride. The upshot is that a method like -`array.reversed()` does not actually copy the array, it simply returns a struct -that points to the back of the array with a negative stride. Arrays adhere to -copy-on-write semantics, so we can cheaply create many read-only references to -the same data, and only need to do copying if we plan to modify data. After -doing a modification, future modifications can be done in-place as long as -there is only one reference to that data. - -Internally, we also take advantage of this inside of tables, which compactly -store all of the key/value pairs in a contiguous array and we can return an -immutable slice of that array showing only the keys or only the values by -choosing the right starting point and stride. - -## Copy on Write - -Arrays can be thought of as values that have copy-on-write semantics that use -reference counting to perform efficient in-place mutations instead of copying -as a performance optimization when it wouldn't affect the program's semantics. -Without getting too deep into the details, suffice it to say that when you -create an array, that array can be thought of as a singular "value" in the same -way that `123` is a value. That variable's value will never change unless you -explicitly perform an assignment operation on the variable or call a method on -the variable. - -Because it would be tedious to require users to write all array operations as -pure functions like `array = array.with_value_at_index(value=x, index=i)`, Tomo -provides the familiar imperative syntax for modifying arrays, but keeps the -semantics of the pure functional style. Writing `array[i] = x` is -_semantically_ equivalent to `array = array.with_value_at_index(value=x, -index=i)`, but much more readable and easy to write. Similarly, -`array.insert(x)` is semantically equivalent to `array = -array.with_value_inserted(x)`. We implement these mutating methods as functions -that take a pointer to an array variable, which then either mutate the array's -data in-place (if this is the only thing referencing that data) or construct a -new array and store its value in the memory where the array variable is stored. - -When there is only a single reference to an array value, we can perform these -modifications in-place (arrays typically have a little bit of spare capacity at -the end, so appending usually doesn't trigger a reallocation). When there are -shared references, we must create a copy of the array's data before modifying -it so the other references don't see the effects of the mutation. Here are some -simple examples: - -```tomo -nums := [10, 20, 30, 39] - -// Efficient in-place mutation because data references are not shared: -nums[4] = 40 - -// Constant time operation, but increments the reference count: -tmp := nums ->> tmp -= [10, 20, 30, 40] - -// Now, a mutation will trigger a copy-on-write, -// which resets the reference count to zero: -nums[4] = 999 ->> nums -= [10, 20, 30, 999] - -// Because of the copy-on-write, `tmp` is unchanged: ->> tmp -= [10, 20, 30, 40] - -// Since the reference count has been reset, we can do more -// mutations without triggering another copy-on-write: -nums[4] = -1 ->> nums -= [10, 20, 30, -1] -``` - -Array reference counting is _approximate_, but will only ever err on the side -of correctness at the expense of performance, not the other way around. -Occasionally, unnecessary copying may occur, but you should never experience an -array value changing because of some operation performed on a different array -value. - -## Array Pointers - -Since the normal case of arrays is to treat them like immutable values, what do -we do if we actually want to have a shared reference to an array whose contents -change over time? In that case, we want to use the `@` operator to create a -pointer to a heap-allocated array and pass that pointer around. This is the same -behavior that you get in Python when you create a `list`: - -```tomo -nums := @[10, 20, 30] -tmp := nums - -nums.insert(40) ->> tmp -= @[10, 20, 30, 40] -``` - -Having multiple pointers to the same heap-allocated array does not cause the -array's reference count to increase, because there is only one "value" in play: -the one stored on the heap. It's only when we store the "value" in multiple -places that we need to increment the reference count: - -```tomo -// Increment the reference count, because `value` now has to hold -// whatever data was at the pointer's location at this point in time: -value := nums[] -``` - -The TL;DR is: you can cheaply modify local variables that aren't aliased or -`@`-allocated arrays, but if you assign a local variable array to another -variable or dereference a heap pointer, it may trigger copy-on-write behavior. - -## Array Methods - -- [`func binary_search(arr: [T], by: func(x,y:&T->Int32) = T.compare -> Int)`](#binary_search) -- [`func by(arr: [T], step: Int -> [T])`](#by) -- [`func clear(arr: @[T] -> Void)`](#clear) -- [`func counts(arr: [T] -> {T=Int})`](#counts) -- [`func find(arr: [T], target: T -> Int?)`](#find) -- [`func first(arr: [T], predicate: func(item:&T -> Bool) -> Int)`](#first) -- [`func from(arr: [T], first: Int -> [T])`](#from) -- [`func has(arr: [T] -> Bool)`](#has) -- [`func heap_pop(arr: @[T], by: func(x,y:&T->Int32) = T.compare -> T?)`](#heap_pop) -- [`func heap_push(arr: @[T], item: T, by=T.compare -> Void)`](#heap_push) -- [`func heapify(arr: @[T], by: func(x,y:&T->Int32) = T.compare -> Void)`](#heapify) -- [`func insert(arr: @[T], item: T, at: Int = 0 -> Void)`](#insert) -- [`func insert_all(arr: @[T], items: [T], at: Int = 0 -> Void)`](#insert_all) -- [`func pop(arr: &[T], index: Int = -1 -> T?)`](#pop) -- [`func random(arr: [T], random: func(min,max:Int64->Int64)? = none -> T)`](#random) -- [`func remove_at(arr: @[T], at: Int = -1, count: Int = 1 -> Void)`](#remove_at) -- [`func remove_item(arr: @[T], item: T, max_count: Int = -1 -> Void)`](#remove_item) -- [`func reversed(arr: [T] -> [T])`](#reversed) -- [`func sample(arr: [T], count: Int, weights: [Num]? = ![Num], random: func(->Num)? = none -> [T])`](#sample) -- [`func shuffle(arr: @[T], random: func(min,max:Int64->Int64)? = none -> Void)`](#shuffle) -- [`func shuffled(arr: [T], random: func(min,max:Int64->Int64)? = none -> [T])`](#shuffled) -- [`func slice(arr: [T], from: Int, to: Int -> [T])`](#slice) -- [`func sort(arr: @[T], by=T.compare -> Void)`](#sort) -- [`sorted(arr: [T], by=T.compare -> [T])`](#sorted) -- [`to(arr: [T], last: Int -> [T])`](#to) -- [`unique(arr: [T] -> {T})`](#unique) - -### `binary_search` -Performs a binary search on a sorted array. - -```tomo -func binary_search(arr: [T], by: func(x,y:&T->Int32) = T.compare -> Int) -``` - -- `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. That is, if the item is found, return its index, otherwise return the -place where it would be found if it were inserted and the array were sorted. - -**Example:** -```tomo ->> [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` -Creates a new array with elements spaced by the specified step value. - -```tomo -func by(arr: [T], step: Int -> [T]) -``` - -- `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:** -```tomo ->> [1, 2, 3, 4, 5, 6].by(2) -= [1, 3, 5] -``` - ---- - -### `clear` -Clears all elements from the array. - -```tomo -func clear(arr: @[T] -> Void) -``` - -- `arr`: The mutable reference to the array to be cleared. - -**Returns:** -Nothing. - -**Example:** -```tomo ->> my_array.clear() -``` - ---- - -### `counts` -Counts the occurrences of each element in the array. - -```tomo -func counts(arr: [T] -> {T=Int}) -``` - -- `arr`: The array to count elements in. - -**Returns:** -A table mapping each element to its count. - -**Example:** -```tomo ->> [10, 20, 30, 30, 30].counts() -= {10=1, 20=1, 30=3} -``` - ---- - -### `find` -Finds the index of the first occurrence of an element (if any). - -```tomo -func find(arr: [T], target: T -> Int?) -``` - -- `arr`: The array to search through. -- `item`: The item to find in the array. - -**Returns:** -The index of the first occurrence or `!Int` if not found. - -**Example:** -```tomo ->> [10, 20, 30, 40, 50].find(20) -= 2 : Int? - ->> [10, 20, 30, 40, 50].find(9999) -= none : Int? -``` - ---- - -### `first` -Find the index of the first item that matches a predicate function (if any). - -```tomo -func first(arr: [T], predicate: func(item:&T -> Bool) -> Int) -``` - -- `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 the index of the first item where the predicate is true or `!Int` if no -item matches. - -**Example:** -```tomo ->> [4, 5, 6].find(func(i:&Int): i.is_prime()) -= 5 : Int? ->> [4, 6, 8].find(func(i:&Int): i.is_prime()) -= none : Int? -``` - ---- - -### `from` -Returns a slice of the array starting from a specified index. - -```tomo -func from(arr: [T], first: Int -> [T]) -``` - -- `arr`: The original array. -- `first`: The index to start from. - -**Returns:** -A new array starting from the specified index. - -**Example:** -```tomo ->> [10, 20, 30, 40, 50].from(3) -= [30, 40, 50] -``` - ---- - -### `has` -Checks if the array has any elements. - -```tomo -func has(arr: [T] -> Bool) -``` - -- `arr`: The array to check. - -**Returns:** -`yes` if the array has elements, `no` otherwise. - -**Example:** -```tomo ->> [10, 20, 30].has(20) -= yes -``` - ---- - -### `heap_pop` -Removes and returns the top element of a heap or `none` if the array is empty. -By default, this is the *minimum* value in the heap. - -```tomo -func heap_pop(arr: @[T], by: func(x,y:&T->Int32) = T.compare -> T?) -``` - -- `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 or `none` if the array is empty. - -**Example:** -```tomo ->> my_heap := [30, 10, 20] ->> my_heap.heapify() ->> my_heap.heap_pop() -= 10 -``` - ---- - -### `heap_push` -Adds an element to the heap and maintains the heap property. By default, this -is a *minimum* heap. - -```tomo -func heap_push(arr: @[T], item: T, by=T.compare -> Void) -``` - -- `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:** -```tomo ->> my_heap.heap_push(10) -``` - ---- - -### `heapify` -Converts an array into a heap. - -```tomo -func heapify(arr: @[T], by: func(x,y:&T->Int32) = T.compare -> Void) -``` - -- `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:** -```tomo ->> my_heap := [30, 10, 20] ->> my_heap.heapify() -``` - ---- - -### `insert` -Inserts an element at a specified position in the array. - -```tomo -func insert(arr: @[T], item: T, at: Int = 0 -> Void) -``` - -- `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:** -```tomo ->> arr := [10, 20] ->> arr.insert(30) ->> arr -= [10, 20, 30] - ->> arr.insert(999, at=2) ->> arr -= [10, 999, 20, 30] -``` - ---- - -### `insert_all` -Inserts an array of items at a specified position in the array. - -```tomo -func insert_all(arr: @[T], items: [T], at: Int = 0 -> Void) -``` - -- `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:** -```tomo -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] -``` - ---- - -### `pop` -Removes and returns an item from the array. If the given index is present in -the array, the item at that index will be removed and the array will become one -element shorter. - -```tomo -func pop(arr: &[T], index: Int = -1 -> T?) -``` - -- `arr`: The array to remove an item from. -- `index`: The index from which to remove the item (default: the last item). - -**Returns:** -`none` if the array is empty or the given index does not exist in the array, -otherwise the item at the given index. - -**Example:** -```tomo ->> arr := [10, 20, 30, 40] - ->> arr.pop() -= 40 ->> arr -= &[10, 20, 30] - ->> arr.pop(index=2) -= 20 ->> arr -= &[10, 30] -``` - ---- - -### `random` -Selects a random element from the array. - -```tomo -func random(arr: [T], random: func(min,max:Int64->Int64)? = none -> T) -``` - -- `arr`: The array from which to select a random element. -- `random`: If provided, this function will be used to get a random index in the array. Returned - values must be between `min` and `max` (inclusive). (Used for deterministic pseudorandom number - generation) - -**Returns:** -A random element from the array. - -**Example:** -```tomo ->> [10, 20, 30].random() -= 20 -``` - ---- - -### `remove_at` -Removes elements from the array starting at a specified index. - -```tomo -func remove_at(arr: @[T], at: Int = -1, count: Int = 1 -> Void) -``` - -- `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:** -```tomo -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` -Removes all occurrences of a specified item from the array. - -```tomo -func remove_item(arr: @[T], item: T, max_count: Int = -1 -> Void) -``` - -- `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:** -```tomo -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` -Returns a reversed slice of the array. - -```tomo -func reversed(arr: [T] -> [T]) -``` - -- `arr`: The array to be reversed. - -**Returns:** -A slice of the array with elements in reverse order. - -**Example:** -```tomo ->> [10, 20, 30].reversed() -= [30, 20, 10] -``` - ---- - -### `sample` -Selects a sample of elements from the array, optionally with weighted -probabilities. - -```tomo -func sample(arr: [T], count: Int, weights: [Num]? = ![Num], random: func(->Num)? = none -> [T]) -``` - -- `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 given, elements will be sampled with uniform - probability. -- `random`: If provided, this function will be used to get random values for - sampling the array. The provided function should return random numbers - between `0.0` (inclusive) and `1.0` (exclusive). (Used for deterministic - pseudorandom number generation) - -**Errors:** -Errors will be raised if any of the following conditions occurs: -- The given array has no elements and `count >= 1` -- `count < 0` (negative count) -- The number of weights provided doesn't match the length of the array. -- Any weight in the weights array is negative, infinite, or `NaN` -- The sum of the given weights is zero (zero probability for every element). - -**Returns:** -A list of sampled elements from the array. - -**Example:** -```tomo ->> [10, 20, 30].sample(2, weights=[90%, 5%, 5%]) -= [10, 10] -``` - ---- - -### `shuffle` -Shuffles the elements of the array in place. - -```tomo -func shuffle(arr: @[T], random: func(min,max:Int64->Int64)? = none -> Void) -``` - -- `arr`: The mutable reference to the array to be shuffled. -- `random`: If provided, this function will be used to get a random index in the array. Returned - values must be between `min` and `max` (inclusive). (Used for deterministic pseudorandom number - generation) - -**Returns:** -Nothing. - -**Example:** -```tomo ->> arr.shuffle() -``` - ---- - -### `shuffled` -Creates a new array with elements shuffled. - -```tomo -func shuffled(arr: [T], random: func(min,max:Int64->Int64)? = none -> [T]) -``` - -- `arr`: The array to be shuffled. -- `random`: If provided, this function will be used to get a random index in the array. Returned - values must be between `min` and `max` (inclusive). (Used for deterministic pseudorandom number - generation) - -**Returns:** -A new array with shuffled elements. - -**Example:** -```tomo ->> [10, 20, 30, 40].shuffled() -= [40, 10, 30, 20] -``` - ---- - -### `slice` -Returns a slice of the array spanning the given indices (inclusive). - -```tomo -func slice(arr: [T], from: Int, to: Int -> [T]) -``` - -- `arr`: The original array. -- `from`: The first index to include. -- `to`: The last index to include. - -**Returns:** -A new array spanning the given indices. Note: negative indices are counted from -the back of the array, so `-1` refers to the last element, `-2` the -second-to-last, and so on. - -**Example:** -```tomo ->> [10, 20, 30, 40, 50].slice(2, 4) -= [20, 30, 40] - ->> [10, 20, 30, 40, 50].slice(-3, -2) -= [30, 40] -``` - ---- - -### `sort` -Sorts the elements of the array in place in ascending order (small to large). - -```tomo -func sort(arr: @[T], by=T.compare -> Void) -``` - -- `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:** -```tomo -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` -Creates a new array with elements sorted. - -```tomo -sorted(arr: [T], by=T.compare -> [T]) -``` - -- `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:** -```tomo ->> [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` -Returns a slice of the array from the start of the original array up to a specified index (inclusive). - -```tomo -to(arr: [T], last: Int -> [T]) -``` - -- `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:** -```tomo ->> [10, 20, 30, 40, 50].to(3) -= [10, 20, 30] - ->> [10, 20, 30, 40, 50].to(-2) -= [10, 20, 30, 40] -``` - ---- - -### `unique` -Returns a Set that contains the unique elements of the array. - -```tomo -unique(arr: [T] -> {T}) -``` - -- `arr`: The array to process. - -**Returns:** -A set containing only unique elements from the array. - -**Example:** -```tomo ->> [10, 20, 10, 10, 30].unique() -= {10, 20, 30} -``` diff --git a/docs/command-line-parsing.md b/docs/command-line-parsing.md index 127cf232..bd71ba41 100644 --- a/docs/command-line-parsing.md +++ b/docs/command-line-parsing.md @@ -97,10 +97,10 @@ foo: Invalid value provided for --foo; valid values are: One Two Signature: foo [--help] <foo> ``` -### Arrays of Text +### Lists of Text -Currently, Tomo supports accepting arguments that take an array of text. -Array-of-text arguments can be passed like this: +Currently, Tomo supports accepting arguments that take a list of text. +List-of-text arguments can be passed like this: ```tomo # many-texts.tm diff --git a/docs/lists.md b/docs/lists.md new file mode 100644 index 00000000..e605eea1 --- /dev/null +++ b/docs/lists.md @@ -0,0 +1,909 @@ +# Lists + +Tomo supports lists as a container type that holds a list of elements of any +type in a compact format similar to a C-style array. Lists are immutable by +default, but use copy-on-write semantics to efficiently mutate in place when +possible. **Lists are 1-indexed**, which means the first item in the list has +index `1`. + +## Syntax + +Lists 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 list, you must specify what type goes inside the list +like this: + +```tomo +empty : [Int] = [] +``` + +For type annotations, a list that holds items with type `T` is written as `[T]`. + +### List Comprehensions + +Lists 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 + +List length can be accessed by the `.length` field: + +```tomo +>> [10, 20, 30].length += 3 +``` + +## Indexing + +List values are accessed using square bracket indexing. Since lists are +1-indexed, the index `1` corresponds to the first item in the list. Negative +indices are used to refer to items from the back of the list, so `-1` is the +last item, `-2` is the second-to-last, and so on. + +```tomo +list := [10, 20, 30, 40] +>> list[1] += 10 + +>> list[2] += 20 + +>> list[-1] += 40 + +>> list[-2] += 30 +``` + +If a list index of `0` or any value larger than the length of the list is +used, it will trigger a runtime error that will print what the invalid list +index was, the length of the list, and a stack trace. As a performance +operation, if list bounds checking proves to be a performance hot spot, you +can explicitly disable bounds checking by adding `list[i; unchecked]` to the +list access. + +## Iteration + +You can iterate over the items in a list like this: + +```tomo +for item in list: + ... + +for i, item in list: + ... +``` + +List iteration operates over the value of the list when the loop began, so +modifying the list during iteration is safe and will not result in the loop +iterating over any of the new values. + +## Concatenation + +Lists can be concatenated with the `++` operator, which returns a list that +has the items from one appended to the other. This should not be confused with +the addition operator `+`, which does not work with lists. + +```tomo +>> [1, 2] ++ [3, 4] += [1, 2, 3, 4] +``` + +## Implementation Details + +Under the hood, lists are implemented as a struct that contains a pointer to a +contiguous chunk of memory storing the elements of the list and some other +metadata. Since Tomo has datatypes with different sizes, like `Bool`s which +take one byte and `struct`s which can take up many bytes, it's worth noting +that lists store the elements compactly and inline, without the need for each +list cell to hold a pointer to where the data actually lives. + +The other metadata stored with a list includes its length as well as the +_stride_ of the list. The stride is not exposed to the user, but it's the gap +in bytes between each element in the list. The reason this is mentioned is +that it is possible to create immutable slices of lists in constant time by +creating a new struct that points to the appropriate starting place for the +list items and has the appropriate stride. The upshot is that a method like +`list.reversed()` does not actually copy the list, it simply returns a struct +that points to the back of the list with a negative stride. Lists adhere to +copy-on-write semantics, so we can cheaply create many read-only references to +the same data, and only need to do copying if we plan to modify data. After +doing a modification, future modifications can be done in-place as long as +there is only one reference to that data. + +Internally, we also take advantage of this inside of tables, which compactly +store all of the key/value pairs in a contiguous list and we can return an +immutable slice of that list showing only the keys or only the values by +choosing the right starting point and stride. + +## Copy on Write + +Lists can be thought of as values that have copy-on-write semantics that use +reference counting to perform efficient in-place mutations instead of copying +as a performance optimization when it wouldn't affect the program's semantics. +Without getting too deep into the details, suffice it to say that when you +create a list, that list can be thought of as a singular "value" in the same +way that `123` is a value. That variable's value will never change unless you +explicitly perform an assignment operation on the variable or call a method on +the variable. + +Because it would be tedious to require users to write all list operations as +pure functions like `list = list.with_value_at_index(value=x, index=i)`, Tomo +provides the familiar imperative syntax for modifying lists, but keeps the +semantics of the pure functional style. Writing `list[i] = x` is +_semantically_ equivalent to `list = list.with_value_at_index(value=x, +index=i)`, but much more readable and easy to write. Similarly, +`list.insert(x)` is semantically equivalent to `list = +list.with_value_inserted(x)`. We implement these mutating methods as functions +that take a pointer to a list variable, which then either mutate the list's +data in-place (if this is the only thing referencing that data) or construct a +new list and store its value in the memory where the list variable is stored. + +When there is only a single reference to a list value, we can perform these +modifications in-place (lists typically have a little bit of spare capacity at +the end, so appending usually doesn't trigger a reallocation). When there are +shared references, we must create a copy of the list's data before modifying +it so the other references don't see the effects of the mutation. Here are some +simple examples: + +```tomo +nums := [10, 20, 30, 39] + +// Efficient in-place mutation because data references are not shared: +nums[4] = 40 + +// Constant time operation, but increments the reference count: +tmp := nums +>> tmp += [10, 20, 30, 40] + +// Now, a mutation will trigger a copy-on-write, +// which resets the reference count to zero: +nums[4] = 999 +>> nums += [10, 20, 30, 999] + +// Because of the copy-on-write, `tmp` is unchanged: +>> tmp += [10, 20, 30, 40] + +// Since the reference count has been reset, we can do more +// mutations without triggering another copy-on-write: +nums[4] = -1 +>> nums += [10, 20, 30, -1] +``` + +List reference counting is _approximate_, but will only ever err on the side +of correctness at the expense of performance, not the other way around. +Occasionally, unnecessary copying may occur, but you should never experience an +list value changing because of some operation performed on a different list +value. + +## List Pointers + +Since the normal case of lists is to treat them like immutable values, what do +we do if we actually want to have a shared reference to a list whose contents +change over time? In that case, we want to use the `@` operator to create a +pointer to a heap-allocated list and pass that pointer around. This is the same +behavior that you get in Python when you create a `list`: + +```tomo +nums := @[10, 20, 30] +tmp := nums + +nums.insert(40) +>> tmp += @[10, 20, 30, 40] +``` + +Having multiple pointers to the same heap-allocated list does not cause the +list's reference count to increase, because there is only one "value" in play: +the one stored on the heap. It's only when we store the "value" in multiple +places that we need to increment the reference count: + +```tomo +// Increment the reference count, because `value` now has to hold +// whatever data was at the pointer's location at this point in time: +value := nums[] +``` + +The TL;DR is: you can cheaply modify local variables that aren't aliased or +`@`-allocated lists, but if you assign a local variable list to another +variable or dereference a heap pointer, it may trigger copy-on-write behavior. + +## List Methods + +- [`func binary_search(list: [T], by: func(x,y:&T->Int32) = T.compare -> Int)`](#binary_search) +- [`func by(list: [T], step: Int -> [T])`](#by) +- [`func clear(list: @[T] -> Void)`](#clear) +- [`func counts(list: [T] -> {T=Int})`](#counts) +- [`func find(list: [T], target: T -> Int?)`](#find) +- [`func first(list: [T], predicate: func(item:&T -> Bool) -> Int)`](#first) +- [`func from(list: [T], first: Int -> [T])`](#from) +- [`func has(list: [T] -> Bool)`](#has) +- [`func heap_pop(list: @[T], by: func(x,y:&T->Int32) = T.compare -> T?)`](#heap_pop) +- [`func heap_push(list: @[T], item: T, by=T.compare -> Void)`](#heap_push) +- [`func heapify(list: @[T], by: func(x,y:&T->Int32) = T.compare -> Void)`](#heapify) +- [`func insert(list: @[T], item: T, at: Int = 0 -> Void)`](#insert) +- [`func insert_all(list: @[T], items: [T], at: Int = 0 -> Void)`](#insert_all) +- [`func pop(list: &[T], index: Int = -1 -> T?)`](#pop) +- [`func random(list: [T], random: func(min,max:Int64->Int64)? = none -> T)`](#random) +- [`func remove_at(list: @[T], at: Int = -1, count: Int = 1 -> Void)`](#remove_at) +- [`func remove_item(list: @[T], item: T, max_count: Int = -1 -> Void)`](#remove_item) +- [`func reversed(list: [T] -> [T])`](#reversed) +- [`func sample(list: [T], count: Int, weights: [Num]? = ![Num], random: func(->Num)? = none -> [T])`](#sample) +- [`func shuffle(list: @[T], random: func(min,max:Int64->Int64)? = none -> Void)`](#shuffle) +- [`func shuffled(list: [T], random: func(min,max:Int64->Int64)? = none -> [T])`](#shuffled) +- [`func slice(list: [T], from: Int, to: Int -> [T])`](#slice) +- [`func sort(list: @[T], by=T.compare -> Void)`](#sort) +- [`sorted(list: [T], by=T.compare -> [T])`](#sorted) +- [`to(list: [T], last: Int -> [T])`](#to) +- [`unique(list: [T] -> {T})`](#unique) + +### `binary_search` +Performs a binary search on a sorted list. + +```tomo +func binary_search(list: [T], by: func(x,y:&T->Int32) = T.compare -> Int) +``` + +- `list`: The sorted list 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 list is sorted according to the given comparison function, +return the index where the given item would be inserted to maintain the sorted +order. That is, if the item is found, return its index, otherwise return the +place where it would be found if it were inserted and the list were sorted. + +**Example:** +```tomo +>> [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` +Creates a new list with elements spaced by the specified step value. + +```tomo +func by(list: [T], step: Int -> [T]) +``` + +- `list`: The original list. +- `step`: The step value for selecting elements. + +**Returns:** +A new list with every `step`-th element from the original list. + +**Example:** +```tomo +>> [1, 2, 3, 4, 5, 6].by(2) += [1, 3, 5] +``` + +--- + +### `clear` +Clears all elements from the list. + +```tomo +func clear(list: @[T] -> Void) +``` + +- `list`: The mutable reference to the list to be cleared. + +**Returns:** +Nothing. + +**Example:** +```tomo +>> my_list.clear() +``` + +--- + +### `counts` +Counts the occurrences of each element in the list. + +```tomo +func counts(list: [T] -> {T=Int}) +``` + +- `list`: The list to count elements in. + +**Returns:** +A table mapping each element to its count. + +**Example:** +```tomo +>> [10, 20, 30, 30, 30].counts() += {10=1, 20=1, 30=3} +``` + +--- + +### `find` +Finds the index of the first occurrence of an element (if any). + +```tomo +func find(list: [T], target: T -> Int?) +``` + +- `list`: The list to search through. +- `item`: The item to find in the list. + +**Returns:** +The index of the first occurrence or `!Int` if not found. + +**Example:** +```tomo +>> [10, 20, 30, 40, 50].find(20) += 2 : Int? + +>> [10, 20, 30, 40, 50].find(9999) += none : Int? +``` + +--- + +### `first` +Find the index of the first item that matches a predicate function (if any). + +```tomo +func first(list: [T], predicate: func(item:&T -> Bool) -> Int) +``` + +- `list`: The list to search through. +- `predicate`: A function that returns `yes` if the item should be returned or + `no` if it should not. + +**Returns:** +Returns the index of the first item where the predicate is true or `!Int` if no +item matches. + +**Example:** +```tomo +>> [4, 5, 6].find(func(i:&Int): i.is_prime()) += 5 : Int? +>> [4, 6, 8].find(func(i:&Int): i.is_prime()) += none : Int? +``` + +--- + +### `from` +Returns a slice of the list starting from a specified index. + +```tomo +func from(list: [T], first: Int -> [T]) +``` + +- `list`: The original list. +- `first`: The index to start from. + +**Returns:** +A new list starting from the specified index. + +**Example:** +```tomo +>> [10, 20, 30, 40, 50].from(3) += [30, 40, 50] +``` + +--- + +### `has` +Checks if the list has any elements. + +```tomo +func has(list: [T] -> Bool) +``` + +- `list`: The list to check. + +**Returns:** +`yes` if the list has elements, `no` otherwise. + +**Example:** +```tomo +>> [10, 20, 30].has(20) += yes +``` + +--- + +### `heap_pop` +Removes and returns the top element of a heap or `none` if the list is empty. +By default, this is the *minimum* value in the heap. + +```tomo +func heap_pop(list: @[T], by: func(x,y:&T->Int32) = T.compare -> T?) +``` + +- `list`: 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 or `none` if the list is empty. + +**Example:** +```tomo +>> my_heap := [30, 10, 20] +>> my_heap.heapify() +>> my_heap.heap_pop() += 10 +``` + +--- + +### `heap_push` +Adds an element to the heap and maintains the heap property. By default, this +is a *minimum* heap. + +```tomo +func heap_push(list: @[T], item: T, by=T.compare -> Void) +``` + +- `list`: 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:** +```tomo +>> my_heap.heap_push(10) +``` + +--- + +### `heapify` +Converts a list into a heap. + +```tomo +func heapify(list: @[T], by: func(x,y:&T->Int32) = T.compare -> Void) +``` + +- `list`: The mutable reference to the list 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:** +```tomo +>> my_heap := [30, 10, 20] +>> my_heap.heapify() +``` + +--- + +### `insert` +Inserts an element at a specified position in the list. + +```tomo +func insert(list: @[T], item: T, at: Int = 0 -> Void) +``` + +- `list`: The mutable reference to the list. +- `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:** +```tomo +>> list := [10, 20] +>> list.insert(30) +>> list += [10, 20, 30] + +>> list.insert(999, at=2) +>> list += [10, 999, 20, 30] +``` + +--- + +### `insert_all` +Inserts a list of items at a specified position in the list. + +```tomo +func insert_all(list: @[T], items: [T], at: Int = 0 -> Void) +``` + +- `list`: The mutable reference to the list. +- `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:** +```tomo +list := [10, 20] +list.insert_all([30, 40]) +>> list += [10, 20, 30, 40] + +list.insert_all([99, 100], at=2) +>> list += [10, 99, 100, 20, 30, 40] +``` + +--- + +### `pop` +Removes and returns an item from the list. If the given index is present in +the list, the item at that index will be removed and the list will become one +element shorter. + +```tomo +func pop(list: &[T], index: Int = -1 -> T?) +``` + +- `list`: The list to remove an item from. +- `index`: The index from which to remove the item (default: the last item). + +**Returns:** +`none` if the list is empty or the given index does not exist in the list, +otherwise the item at the given index. + +**Example:** +```tomo +>> list := [10, 20, 30, 40] + +>> list.pop() += 40 +>> list += &[10, 20, 30] + +>> list.pop(index=2) += 20 +>> list += &[10, 30] +``` + +--- + +### `random` +Selects a random element from the list. + +```tomo +func random(list: [T], random: func(min,max:Int64->Int64)? = none -> T) +``` + +- `list`: The list from which to select a random element. +- `random`: If provided, this function will be used to get a random index in the list. Returned + values must be between `min` and `max` (inclusive). (Used for deterministic pseudorandom number + generation) + +**Returns:** +A random element from the list. + +**Example:** +```tomo +>> [10, 20, 30].random() += 20 +``` + +--- + +### `remove_at` +Removes elements from the list starting at a specified index. + +```tomo +func remove_at(list: @[T], at: Int = -1, count: Int = 1 -> Void) +``` + +- `list`: The mutable reference to the list. +- `at`: The index at which to start removing elements (default is `-1`, which means the end of the list). +- `count`: The number of elements to remove (default is `1`). + +**Returns:** +Nothing. + +**Example:** +```tomo +list := [10, 20, 30, 40, 50] +list.remove_at(2) +>> list += [10, 30, 40, 50] + +list.remove_at(2, count=2) +>> list += [10, 50] +``` + +--- + +### `remove_item` +Removes all occurrences of a specified item from the list. + +```tomo +func remove_item(list: @[T], item: T, max_count: Int = -1 -> Void) +``` + +- `list`: The mutable reference to the list. +- `item`: The item to be removed. +- `max_count`: The maximum number of occurrences to remove (default is `-1`, meaning all occurrences). + +**Returns:** +Nothing. + +**Example:** +```tomo +list := [10, 20, 10, 20, 30] +list.remove_item(10) +>> list += [20, 20, 30] + +list.remove_item(20, max_count=1) +>> list += [20, 30] +``` + +--- + +### `reversed` +Returns a reversed slice of the list. + +```tomo +func reversed(list: [T] -> [T]) +``` + +- `list`: The list to be reversed. + +**Returns:** +A slice of the list with elements in reverse order. + +**Example:** +```tomo +>> [10, 20, 30].reversed() += [30, 20, 10] +``` + +--- + +### `sample` +Selects a sample of elements from the list, optionally with weighted +probabilities. + +```tomo +func sample(list: [T], count: Int, weights: [Num]? = ![Num], random: func(->Num)? = none -> [T]) +``` + +- `list`: The list to sample from. +- `count`: The number of elements to sample. +- `weights`: The probability weights for each element in the list. These + values do not need to add up to any particular number, they are relative + weights. If no weights are given, elements will be sampled with uniform + probability. +- `random`: If provided, this function will be used to get random values for + sampling the list. The provided function should return random numbers + between `0.0` (inclusive) and `1.0` (exclusive). (Used for deterministic + pseudorandom number generation) + +**Errors:** +Errors will be raised if any of the following conditions occurs: +- The given list has no elements and `count >= 1` +- `count < 0` (negative count) +- The number of weights provided doesn't match the length of the list. +- Any weight in the weights list is negative, infinite, or `NaN` +- The sum of the given weights is zero (zero probability for every element). + +**Returns:** +A list of sampled elements from the list. + +**Example:** +```tomo +>> [10, 20, 30].sample(2, weights=[90%, 5%, 5%]) += [10, 10] +``` + +--- + +### `shuffle` +Shuffles the elements of the list in place. + +```tomo +func shuffle(list: @[T], random: func(min,max:Int64->Int64)? = none -> Void) +``` + +- `list`: The mutable reference to the list to be shuffled. +- `random`: If provided, this function will be used to get a random index in the list. Returned + values must be between `min` and `max` (inclusive). (Used for deterministic pseudorandom number + generation) + +**Returns:** +Nothing. + +**Example:** +```tomo +>> list.shuffle() +``` + +--- + +### `shuffled` +Creates a new list with elements shuffled. + +```tomo +func shuffled(list: [T], random: func(min,max:Int64->Int64)? = none -> [T]) +``` + +- `list`: The list to be shuffled. +- `random`: If provided, this function will be used to get a random index in the list. Returned + values must be between `min` and `max` (inclusive). (Used for deterministic pseudorandom number + generation) + +**Returns:** +A new list with shuffled elements. + +**Example:** +```tomo +>> [10, 20, 30, 40].shuffled() += [40, 10, 30, 20] +``` + +--- + +### `slice` +Returns a slice of the list spanning the given indices (inclusive). + +```tomo +func slice(list: [T], from: Int, to: Int -> [T]) +``` + +- `list`: The original list. +- `from`: The first index to include. +- `to`: The last index to include. + +**Returns:** +A new list spanning the given indices. Note: negative indices are counted from +the back of the list, so `-1` refers to the last element, `-2` the +second-to-last, and so on. + +**Example:** +```tomo +>> [10, 20, 30, 40, 50].slice(2, 4) += [20, 30, 40] + +>> [10, 20, 30, 40, 50].slice(-3, -2) += [30, 40] +``` + +--- + +### `sort` +Sorts the elements of the list in place in ascending order (small to large). + +```tomo +func sort(list: @[T], by=T.compare -> Void) +``` + +- `list`: The mutable reference to the list 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:** +```tomo +list := [40, 10, -30, 20] +list.sort() +>> list += [-30, 10, 20, 40] + +list.sort(func(a,b:&Int): a.abs() <> b.abs()) +>> list += [10, 20, -30, 40] +``` + +--- + +### `sorted` +Creates a new list with elements sorted. + +```tomo +sorted(list: [T], by=T.compare -> [T]) +``` + +- `list`: The list 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 list with sorted elements. + +**Example:** +```tomo +>> [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` +Returns a slice of the list from the start of the original list up to a specified index (inclusive). + +```tomo +to(list: [T], last: Int -> [T]) +``` + +- `list`: The original list. +- `last`: The index up to which elements should be included. + +**Returns:** +A new list containing elements from the start up to the specified index. + +**Example:** +```tomo +>> [10, 20, 30, 40, 50].to(3) += [10, 20, 30] + +>> [10, 20, 30, 40, 50].to(-2) += [10, 20, 30, 40] +``` + +--- + +### `unique` +Returns a Set that contains the unique elements of the list. + +```tomo +unique(list: [T] -> {T}) +``` + +- `list`: The list to process. + +**Returns:** +A set containing only unique elements from the list. + +**Example:** +```tomo +>> [10, 20, 10, 10, 30].unique() += {10, 20, 30} +``` diff --git a/docs/metamethods.md b/docs/metamethods.md index 0983aff0..f47c8233 100644 --- a/docs/metamethods.md +++ b/docs/metamethods.md @@ -31,17 +31,17 @@ enums. At this time, metamethods may not be overridden. ## Generic Metamethods -Due to the presence of pointers, arrays, tables, and functions, there are +Due to the presence of pointers, lists, tables, and functions, there are potentially a very large number of metamethods that would be required if _every_ type had its own set of metamethods. To reduce the amount of generated code, Tomo uses generic metamethods, which are general-purpose functions that take an object pointer and a type info struct pointer that has metadata about the object's type. That metadata is added automatically at compile time and -used to perform the appropriate operations. As an example, every array follows +used to perform the appropriate operations. As an example, every list follows the same logic when performing comparisons, except that each item is compared -using the item's comparison function. Therefore, we can compile a single array -comparison function and reuse it for each type of array if we pass in some -metadata about how to compare the array's items. +using the item's comparison function. Therefore, we can compile a single list +comparison function and reuse it for each type of list if we pass in some +metadata about how to compare the list's items. When possible, we avoid calling metamethods (for example, doing fixed-sized integer comparisons does not require calling a function), but metamethods are diff --git a/docs/nums.md b/docs/nums.md index 36dedcac..34e513ef 100644 --- a/docs/nums.md +++ b/docs/nums.md @@ -27,7 +27,7 @@ differentiate between possibly-NaN values and definitely-not-NaN values. Tomo has a separate concept for expressing the lack of a defined value: optional types. Consequently, Tomo has merged these two concepts, so `NaN` is called `none` and has the type `Num?` or `Num32?`. In this way, it's no -different from optional integers or optional arrays. This means that if a +different from optional integers or optional lists. This means that if a variable has type `Num`, it is guaranteed to not hold a NaN value. This also means that operations which may produce NaN values have a result type of `Num?`. For example, division can take two non-NaN values and return a result diff --git a/docs/operators.md b/docs/operators.md index 548891e7..d76d625d 100644 --- a/docs/operators.md +++ b/docs/operators.md @@ -7,8 +7,8 @@ Tomo supports a number of operators, both infix and prefix: - `^`: exponentiation for integers and floating point numbers - `mod`: modulus for integers and floating point numbers - `mod1`: clock-style modulus, which is equivalent to `1 + ((x-1) mod y)`. This - is particularly useful for doing wraparound behavior on 1-indexed arrays. -- `++`: concatenation (for text and arrays) + is particularly useful for doing wraparound behavior on 1-indexed lists. +- `++`: concatenation (for text and lists) - `<<`, `>>`: bitwise left shift and right shift for integers - `<<<`, `>>>`: unsigned bitwise left shift and right shift for integers - `_min_`/`_max_`: minimum and maximum (see below) @@ -33,7 +33,7 @@ value to the user. = 1[32] ``` -It's particularly handy for using the array `sort()` method, which takes a +It's particularly handy for using the list `sort()` method, which takes a function that returns a signed integer: ```tomo @@ -153,16 +153,16 @@ struct Person(name:Text, age:Int) >> Person("Alice", 33) _max_.age Person("Bob", 20) = Person(name="Alice", age=33) -// Get the longest of two arrays: +// Get the longest of two lists: >> [10, 20, 30, 40] _max_.length [99, 1] = [10, 20, 30, 40] -// Get the array with the highest value in the last position: +// Get the list with the highest value in the last position: >> [10, 20, 999] _max_[-1] [99, 1] = [10, 20, 999] ``` -The keyed comparison can chain together multiple field accesses, array index +The keyed comparison can chain together multiple field accesses, list index operations, method calls, etc. If you wanted to, for example, get the item whose `x` field has the highest absolute value, you could use `_max_.x.abs()`. diff --git a/docs/optionals.md b/docs/optionals.md index 7b100dc7..48e48875 100644 --- a/docs/optionals.md +++ b/docs/optionals.md @@ -32,7 +32,7 @@ conventions and which would generate a lot of unnecessary code. ## Syntax Optional types are written using a `?` after the type name. So, an optional -integer would be written as `Int?` and an optional array of texts would be +integer would be written as `Int?` and an optional list of texts would be written as `[Text]?`. None can be written explicitly using `none` with a type annotation. For @@ -43,7 +43,7 @@ value or `none` and initialize it as none, you would write it as: x : Int = none ``` -Similarly, if you wanted to declare a variable that could be an array of texts +Similarly, if you wanted to declare a variable that could be a list of texts or none and initialize it as none, you would write: ```tomo @@ -127,10 +127,10 @@ for line in lines: ## Implementation Notes The implementation of optional types is highly efficient and has no memory -overhead for pointers, collection types (arrays, sets, tables), booleans, +overhead for pointers, collection types (lists, sets, tables), booleans, texts, enums, nums, or integers (`Int` type only). This is done by using carefully chosen values, such as `0` for pointers, `2` for booleans, or a -negative length for arrays. However, for fixed-size integers (`Int64`, `Int32`, +negative length for lists. However, for fixed-size integers (`Int64`, `Int32`, `Int16`, and `Int8`), bytes, and structs, an additional byte is required for out-of-band information about whether the value is none or not. diff --git a/docs/paths.md b/docs/paths.md index af179ede..cde7f74d 100644 --- a/docs/paths.md +++ b/docs/paths.md @@ -452,13 +452,13 @@ A list of file paths. --- ### `from_components` -Returns a path built from an array of path components. +Returns a path built from a list of path components. ```tomo func from_components(components: [Text] -> Path) ``` -- `components`: An array of path components. +- `components`: A list of path components. **Returns:** A path representing the given components. @@ -476,7 +476,7 @@ A path representing the given components. --- ### `glob` -Perform a globbing operation and return an array of matching paths. Some glob +Perform a globbing operation and return a list of matching paths. Some glob specific details: - The paths "." and ".." are *not* included in any globbing results. @@ -510,7 +510,7 @@ A list of file paths that match the glob. >> (./.*).glob() = [(./.hidden)] -# Globs with no matches return an empty array: +# Globs with no matches return an empty list: >> (./*.xxx).glob() = [] ``` @@ -915,7 +915,7 @@ func write(path: Path, bytes: [Byte], permissions=0o644[32] -> Void) ``` - `path`: The path of the file to write to. -- `bytes`: An array of bytes to write to the file. +- `bytes`: A list of bytes to write to the file. - `permissions`: The permissions to set on the file if it is created (default is `0o644`). **Returns:** diff --git a/docs/pointers.md b/docs/pointers.md index 36cab2c1..0644a6ab 100644 --- a/docs/pointers.md +++ b/docs/pointers.md @@ -13,8 +13,8 @@ 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 + nums[1] = 10 // This performs a copy-on-write and creates a new list + // The new list is only accessible as a local variable here ... my_nums := [0, 1, 2] no_mutation_possible(my_nums) @@ -93,17 +93,17 @@ else: 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 +to use `ptr[].foo`. The same is true for list accesses like `ptr[i]` and method calls like `ptr.reversed()`. # Read-Only Views -In a small number of API methods (`array.first()`, `array.binary_search()`, -`array.sort()`, `array.sorted()`, and `array.heapify()`), the methods allow you +In a small number of API methods (`list.first()`, `list.binary_search()`, +`list.sort()`, `list.sorted()`, and `list.heapify()`), the methods allow you to provide custom comparison functions. However, for safety, we don't actually want the comparison methods to be able mutate the values inside of immutable -array values. For implementation reasons, we can't pass the values themselves -to the comparison functions, but need to pass pointers to the array members. +list values. For implementation reasons, we can't pass the values themselves +to the comparison functions, but need to pass pointers to the list members. So, to work around this, Tomo allows you to define functions that take immutable view pointers as arguments. These behave similarly to `@` pointers, but their type signature uses `&` instead of `@` and read-only view pointers diff --git a/docs/reductions.md b/docs/reductions.md index 3cbb6d5d..abd612b0 100644 --- a/docs/reductions.md +++ b/docs/reductions.md @@ -102,7 +102,7 @@ nums := [1, 2, -3] ## Comprehensions -Reductions work not only with iterable values (arrays, sets, integers, etc.), +Reductions work not only with iterable values (lists, sets, integers, etc.), but also with comprehensions. You can use comprehensions to perform reductions while filtering out values or while applying a transformation: diff --git a/docs/serialization.md b/docs/serialization.md index d63cb168..a938e316 100644 --- a/docs/serialization.md +++ b/docs/serialization.md @@ -11,7 +11,7 @@ original value. ## Serializing To serialize data, simply call the method `.serialized()` on any value and it -will return an array of bytes that encode the value's data: +will return a list of bytes that encode the value's data: ```tomo value := Int64(5) @@ -19,7 +19,7 @@ value := Int64(5) = [0x0A] : [Byte] ``` -Serialization produces a fairly compact representation of data as a flat array +Serialization produces a fairly compact representation of data as a flat list of bytes. In this case, a 64-bit integer can be represented in a single byte because it's a small number. @@ -72,4 +72,4 @@ only 9 bytes for the whole thing! Unfortunately, not all types can be easily serialized. In particular, types and functions cannot be serialized because their data contents cannot be easily -converted to portable byte arrays. All other datatypes _can_ be serialized. +converted to portable byte lists. All other datatypes _can_ be serialized. diff --git a/docs/sets.md b/docs/sets.md index 88e47e98..1aaba59d 100644 --- a/docs/sets.md +++ b/docs/sets.md @@ -28,7 +28,7 @@ For type annotations, a set that holds items with type `T` is written as `|T|`. ### Comprehensions -Similar to arrays, sets can use comprehensions: +Similar to lists, sets can use comprehensions: ```tomo set := |10*i for i in 10| @@ -38,7 +38,7 @@ set3 := |-10, 10*i for i in 10| ## Accessing Items -Sets internally store their items in an array, which you can access with the +Sets internally store their items in a list, which you can access with the `.items` field. This is a constant-time operation that produces an immutable view: @@ -115,7 +115,7 @@ func add_all(set:@|T|, items: [T] -> Void) ``` - `set`: The mutable reference to the set. -- `items`: The array of items to add to the set. +- `items`: The list of items to add to the set. **Returns:** Nothing. @@ -257,7 +257,7 @@ func remove_all(set:@|T|, items: [T] -> Void) ``` - `set`: The mutable reference to the set. -- `items`: The array of items to remove from the set. +- `items`: The list of items to remove from the set. **Returns:** Nothing. diff --git a/docs/tables.md b/docs/tables.md index 7a50c9dd..605a1486 100644 --- a/docs/tables.md +++ b/docs/tables.md @@ -2,7 +2,7 @@ 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 +insertion order and has fast access to keys and values as list slices. Tables support *all* types as both keys and values. ## Syntax @@ -25,7 +25,7 @@ For type annotations, a table that maps keys with type `K` to values of type ### Comprehensions -Similar to arrays, tables can use comprehensions to dynamically construct tables: +Similar to lists, tables can use comprehensions to dynamically construct tables: ```tomo t := {i=10*i for i in 10} @@ -123,7 +123,7 @@ Table length can be accessed by the `.length` field: ## Accessing Keys and Values -The keys and values of a table can be efficiently accessed as arrays using a +The keys and values of a table can be efficiently accessed as lists using a constant-time immutable slice of the internal data from the table: ```tomo diff --git a/docs/text.md b/docs/text.md index f0665762..d3063443 100644 --- a/docs/text.md +++ b/docs/text.md @@ -3,7 +3,7 @@ `Text` is Tomo's datatype to represent text. The name `Text` is used instead of "string" because Tomo text represents immutable, normalized unicode data with fast indexing that has an implementation that is efficient for concatenation. -These are _not_ C-style NUL-terminated character arrays. GNU libunistring is +These are _not_ C-style NUL-terminated character lists. GNU libunistring is used for full Unicode functionality (grapheme cluster counts, capitalization, etc.). @@ -12,8 +12,8 @@ etc.). Internally, Tomo text's implementation is based on [Raku/MoarVM's strings](https://docs.raku.org/language/unicode) and [Boehm et al's Cords](https://www.cs.tufts.edu/comp/150FP/archive/hans-boehm/ropes.pdf). -Strings store their grapheme cluster count and either a compact array of 8-bit -ASCII characters (for ASCII text), an array of 32-bit normal-form grapheme +Strings store their grapheme cluster count and either a compact list of 8-bit +ASCII characters (for ASCII text), a list of 32-bit normal-form grapheme cluster values (see below), or a (roughly) balanced binary tree concatenation of two texts. The upside is that repeated concatenations are typically a constant-time operation, which will occasionally require a small rebalancing @@ -33,7 +33,7 @@ non-ASCII text is stored as 32-bit normal-form graphemes. A normal-form grapheme is either a positive value representing a Unicode codepoint that corresponds to a grapheme cluster (most Unicode letters used in natural language fall into this category after normalization) or a negative value -representing an index into an internal array of "synthetic grapheme cluster +representing an index into an internal list of "synthetic grapheme cluster codepoints." Here are some examples: - `A` is a normal codepoint that is also a grapheme cluster, so it would @@ -223,7 +223,7 @@ shorthand for `${}"foo"`. Singly quoted text with no dollar sign (e.g. Concatenation in the typical case is a fast operation: `"{x}{y}"` or `x ++ y`. Because text concatenation is typically fast, there is no need for a separate -"string builder" class in the language and no need to use an array of text +"string builder" class in the language and no need to use a list of text fragments. ### Text Length @@ -433,7 +433,7 @@ for chunk in text.by_split_any(",;"): --- ### `bytes` -Converts a `Text` value to an array of bytes representing a UTF8 encoding of +Converts a `Text` value to a list of bytes representing a UTF8 encoding of the text. ```tomo @@ -443,7 +443,7 @@ func bytes(text: Text -> [Byte]) - `text`: The text to be converted to UTF8 bytes. **Returns:** -An array of bytes (`[Byte]`) representing the text in UTF8 encoding. +A list of bytes (`[Byte]`) representing the text in UTF8 encoding. **Example:** ```tomo @@ -481,7 +481,7 @@ func caseless_equals(a: Text, b:Text, language:Text = "C" -> Bool) --- ### `codepoint_names` -Returns an array of the names of each codepoint in the text. +Returns a list of the names of each codepoint in the text. ```tomo func codepoint_names(text: Text -> [Text]) @@ -490,7 +490,7 @@ func codepoint_names(text: Text -> [Text]) - `text`: The text from which to extract codepoint names. **Returns:** -An array of codepoint names (`[Text]`). +A list of codepoint names (`[Text]`). **Example:** ```tomo @@ -664,14 +664,14 @@ func has(text: Text, target: Text -> Bool) --- ### `join` -Joins an array of text pieces with a specified glue. +Joins a list of text pieces with a specified glue. ```tomo func join(glue: Text, pieces: [Text] -> Text) ``` - `glue`: The text used to join the pieces. -- `pieces`: The array of text pieces to be joined. +- `pieces`: The list of text pieces to be joined. **Returns:** A single `Text` value with the pieces joined by the glue. @@ -739,7 +739,7 @@ exact desired length. --- ### `lines` -Splits the text into an array of lines of text, preserving blank lines, +Splits the text into a list of lines of text, preserving blank lines, ignoring trailing newlines, and handling `\r\n` the same as `\n`. ```tomo @@ -749,7 +749,7 @@ func lines(text: Text -> [Text]) - `text`: The text to be split into lines. **Returns:** -An array of substrings resulting from the split. +A list of substrings resulting from the split. **Example:** ```tomo @@ -935,7 +935,7 @@ the text. --- ### `split` -Splits the text into an array of substrings based on exact matches of a delimiter. +Splits the text into a list of substrings based on exact matches of a delimiter. **Note:** to split based on a set of delimiter characters, use [`split_any()`](#split_any). ```tomo @@ -947,7 +947,7 @@ func split(text: Text, delimiter: Text = "" -> [Text]) empty text, the text will be split into individual grapheme clusters. **Returns:** -An array of subtexts resulting from the split. +A list of subtexts resulting from the split. **Example:** ```tomo @@ -961,7 +961,7 @@ An array of subtexts resulting from the split. --- ### `split_any` -Splits the text into an array of substrings at one or more occurrences of a set +Splits the text into a list of substrings at one or more occurrences of a set of delimiter characters (grapheme clusters). **Note:** to split based on an exact delimiter, use [`split()`](#split). @@ -974,7 +974,7 @@ func split_any(text: Text, delimiters: Text = " $\t\r\n" -> [Text]) splitting the text into chunks. **Returns:** -An array of subtexts resulting from the split. +A list of subtexts resulting from the split. **Example:** ```tomo @@ -1144,7 +1144,7 @@ The uppercase version of the text. --- ### `utf32_codepoints` -Returns an array of Unicode code points for UTF32 encoding of the text. +Returns a list of Unicode code points for UTF32 encoding of the text. ```tomo func utf32_codepoints(text: Text -> [Int32]) @@ -1153,7 +1153,7 @@ func utf32_codepoints(text: Text -> [Int32]) - `text`: The text from which to extract Unicode code points. **Returns:** -An array of 32-bit integer Unicode code points (`[Int32]`). +A list of 32-bit integer Unicode code points (`[Int32]`). **Example:** ```tomo diff --git a/examples/commands/commands.c b/examples/commands/commands.c index f823eeb0..973fc0c5 100644 --- a/examples/commands/commands.c +++ b/examples/commands/commands.c @@ -15,8 +15,8 @@ #define READ_END 0 #define WRITE_END 1 -int run_command(Text_t exe, Array_t arg_array, Table_t env_table, - OptionalArray_t input_bytes, Array_t *output_bytes, Array_t *error_bytes) +int run_command(Text_t exe, List_t arg_list, Table_t env_table, + OptionalList_t input_bytes, List_t *output_bytes, List_t *error_bytes) { pthread_testcancel(); @@ -57,28 +57,28 @@ int run_command(Text_t exe, Array_t arg_array, Table_t env_table, const char *exe_str = Text$as_c_string(exe); - Array_t arg_strs = {}; - Array$insert_value(&arg_strs, exe_str, I(0), sizeof(char*)); - for (int64_t i = 0; i < arg_array.length; i++) - Array$insert_value(&arg_strs, Text$as_c_string(*(Text_t*)(arg_array.data + i*arg_array.stride)), I(0), sizeof(char*)); - Array$insert_value(&arg_strs, NULL, I(0), sizeof(char*)); + List_t arg_strs = {}; + List$insert_value(&arg_strs, exe_str, I(0), sizeof(char*)); + for (int64_t i = 0; i < arg_list.length; i++) + List$insert_value(&arg_strs, Text$as_c_string(*(Text_t*)(arg_list.data + i*arg_list.stride)), I(0), sizeof(char*)); + List$insert_value(&arg_strs, NULL, I(0), sizeof(char*)); char **args = arg_strs.data; extern char **environ; char **env = environ; if (env_table.entries.length > 0) { - Array_t env_array = {}; // Array of const char* + List_t env_list = {}; // List of const char* for (char **e = environ; *e; e++) - Array$insert(&env_array, e, I(0), sizeof(char*)); + List$insert(&env_list, e, I(0), sizeof(char*)); for (int64_t i = 0; i < env_table.entries.length; i++) { struct { Text_t key, value; } *entry = env_table.entries.data + env_table.entries.stride*i; const char *env_entry = String(entry->key, "=", entry->value); - Array$insert(&env_array, &env_entry, I(0), sizeof(char*)); + List$insert(&env_list, &env_entry, I(0), sizeof(char*)); } - Array$insert_value(&env_array, NULL, I(0), sizeof(char*)); - assert(env_array.stride == sizeof(char*)); - env = env_array.data; + List$insert_value(&env_list, NULL, I(0), sizeof(char*)); + assert(env_list.stride == sizeof(char*)); + env = env_list.data; } pid_t pid; @@ -101,11 +101,11 @@ int run_command(Text_t exe, Array_t arg_array, Table_t env_table, if (error_bytes) pollfds[2] = (struct pollfd){.fd=child_errpipe[WRITE_END], .events=POLLIN}; if (input_bytes.length > 0 && input_bytes.stride != 1) - Array$compact(&input_bytes, sizeof(char)); + List$compact(&input_bytes, sizeof(char)); if (output_bytes) - *output_bytes = (Array_t){.atomic=1, .stride=1, .length=0}; + *output_bytes = (List_t){.atomic=1, .stride=1, .length=0}; if (error_bytes) - *error_bytes = (Array_t){.atomic=1, .stride=1, .length=0}; + *error_bytes = (List_t){.atomic=1, .stride=1, .length=0}; while (input_bytes.length > 0 || output_bytes || error_bytes) { (void)poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1); // Wait for data or readiness @@ -223,7 +223,7 @@ static Text_t _next_line(child_info_t *child) return line_text; } -OptionalClosure_t command_by_line(Text_t exe, Array_t arg_array, Table_t env_table) +OptionalClosure_t command_by_line(Text_t exe, List_t arg_list, Table_t env_table) { posix_spawnattr_t attr; posix_spawnattr_init(&attr); @@ -238,28 +238,28 @@ OptionalClosure_t command_by_line(Text_t exe, Array_t arg_array, Table_t env_tab const char *exe_str = Text$as_c_string(exe); - Array_t arg_strs = {}; - Array$insert_value(&arg_strs, exe_str, I(0), sizeof(char*)); - for (int64_t i = 0; i < arg_array.length; i++) - Array$insert_value(&arg_strs, Text$as_c_string(*(Text_t*)(arg_array.data + i*arg_array.stride)), I(0), sizeof(char*)); - Array$insert_value(&arg_strs, NULL, I(0), sizeof(char*)); + List_t arg_strs = {}; + List$insert_value(&arg_strs, exe_str, I(0), sizeof(char*)); + for (int64_t i = 0; i < arg_list.length; i++) + List$insert_value(&arg_strs, Text$as_c_string(*(Text_t*)(arg_list.data + i*arg_list.stride)), I(0), sizeof(char*)); + List$insert_value(&arg_strs, NULL, I(0), sizeof(char*)); char **args = arg_strs.data; extern char **environ; char **env = environ; if (env_table.entries.length > 0) { - Array_t env_array = {}; // Array of const char* + List_t env_list = {}; // List of const char* for (char **e = environ; *e; e++) - Array$insert(&env_array, e, I(0), sizeof(char*)); + List$insert(&env_list, e, I(0), sizeof(char*)); for (int64_t i = 0; i < env_table.entries.length; i++) { struct { Text_t key, value; } *entry = env_table.entries.data + env_table.entries.stride*i; const char *env_entry = String(entry->key, "=", entry->value); - Array$insert(&env_array, &env_entry, I(0), sizeof(char*)); + List$insert(&env_list, &env_entry, I(0), sizeof(char*)); } - Array$insert_value(&env_array, NULL, I(0), sizeof(char*)); - assert(env_array.stride == sizeof(char*)); - env = env_array.data; + List$insert_value(&env_list, NULL, I(0), sizeof(char*)); + assert(env_list.stride == sizeof(char*)); + env = env_list.data; } pid_t pid; diff --git a/examples/http-server/http-server.tm b/examples/http-server/http-server.tm index 5b73d1af..80774bff 100644 --- a/examples/http-server/http-server.tm +++ b/examples/http-server/http-server.tm @@ -38,7 +38,7 @@ func serve(port:Int32, handler:func(request:HTTPRequest -> HTTPResponse), num_th response := handler(request).bytes() C_code { if (@response.stride != 1) - Array$compact(&@response, 1); + List$compact(&@response, 1); write(@connection, @response.data, @response.length); close(@connection); } diff --git a/examples/learnxiny.tm b/examples/learnxiny.tm index b8eaa77e..9b9f1017 100644 --- a/examples/learnxiny.tm +++ b/examples/learnxiny.tm @@ -52,23 +52,23 @@ func main() else say("else") - # Arrays: + # Lists: my_numbers := [10, 20, 30] - # Empty arrays require specifying the type: - empty_array : [Int] - >> empty_array.length + # Empty lists require specifying the type: + empty_list : [Int] + >> empty_list.length = 0 - # Arrays are 1-indexed, so the first element is at index 1: + # Lists are 1-indexed, so the first element is at index 1: >> my_numbers[1] = 10 - # Negative indices can be used to get items from the back of the array: + # Negative indices can be used to get items from the back of the list: >> my_numbers[-1] = 30 - # If an invalid index outside the array's bounds is used (e.g. + # If an invalid index outside the list's bounds is used (e.g. # my_numbers[999]), an error message will be printed and the program will # exit. @@ -80,7 +80,7 @@ func main() for index, num in my_numbers pass # Pass means "do nothing" - # Arrays can be created with array comprehensions, which are loops: + # Lists can be created with list comprehensions, which are loops: >> [x*10 for x in my_numbers] = [100, 200, 300] >> [x*10 for x in my_numbers if x != 20] @@ -132,7 +132,7 @@ func main() pass # Tables also have ".keys" and ".values" fields to explicitly access the - # array of keys or values in the table. + # list of keys or values in the table. >> table.keys = ["one", "two"] >> table.values @@ -153,7 +153,7 @@ func main() # If no default is provided and a missing key is looked up, the program # will print an error message and halt. - # Any types can be used in tables, for example, a table mapping arrays to + # Any types can be used in tables, for example, a table mapping lists to # strings: table3 := {[10, 20]="one", [30, 40, 50]="two"} >> table3[[10, 20]]! @@ -241,10 +241,10 @@ func takes_many_types( integer:Int, floating_point_number:Num, text_aka_string:Text, - array_of_ints:[Int], + list_of_ints:[Int], table_of_text_to_bools:{Text=Bool}, set_of_ints:|Int|, - pointer_to_mutable_array_of_ints:@[Int], + pointer_to_mutable_list_of_ints:@[Int], optional_int:Int?, function_from_int_to_text:func(x:Int -> Text), ) diff --git a/examples/patterns/README.md b/examples/patterns/README.md index 8e2a1ff8..faf2854e 100644 --- a/examples/patterns/README.md +++ b/examples/patterns/README.md @@ -35,7 +35,7 @@ Pattern matching functions work with a type called `PatternMatch` that has three - `text`: The full text of the match. - `index`: The index in the text where the match was found. -- `captures`: An array containing the matching text of each non-literal pattern group. +- `captures`: A list containing the matching text of each non-literal pattern group. See [Text Functions](text.md#Text-Functions) for the full API documentation. @@ -229,7 +229,7 @@ func find_patterns(text:Text, pattern:Pat -> [PatternMatch]) - `pattern`: The pattern to match. **Returns:** -An array of `PatternMatch` objects. +A list of `PatternMatch` objects. **Example:** ```tomo @@ -314,7 +314,7 @@ func matches_pattern(text:Text, pattern:Pat -> Bool) --- ### `pattern_captures` -Returns an array of pattern captures for the given pattern. +Returns a list of pattern captures for the given pattern. ```tomo func pattern_captures(text:Text, pattern:Pat -> [Text]?) @@ -324,7 +324,7 @@ func pattern_captures(text:Text, pattern:Pat -> [Text]?) - `pattern`: The pattern to match. **Returns:** -An optional array of matched pattern captures. Returns `none` if the text does +An optional list of matched pattern captures. Returns `none` if the text does not match the pattern. **Example:** @@ -384,7 +384,7 @@ func split_pattern(text:Text, pattern:Pat -> [Text]) - `pattern`: The pattern to use as a separator. **Returns:** -An array of text segments. +A list of text segments. **Example:** ```tomo diff --git a/examples/patterns/match_type.h b/examples/patterns/match_type.h index 5d063431..abbc4fce 100644 --- a/examples/patterns/match_type.h +++ b/examples/patterns/match_type.h @@ -3,6 +3,6 @@ typedef struct { Text_t text; Int_t index; - Array_t captures; + List_t captures; } XMatch; diff --git a/examples/patterns/patterns.c b/examples/patterns/patterns.c index 180ab431..ee27e4e3 100644 --- a/examples/patterns/patterns.c +++ b/examples/patterns/patterns.c @@ -12,13 +12,13 @@ typedef struct { Text_t text; Int_t index; - Array_t captures; + List_t captures; } PatternMatch; typedef struct { Text_t text; Int_t index; - Array_t captures; + List_t captures; bool is_none:1; } OptionalPatternMatch; @@ -42,7 +42,7 @@ typedef struct { }; } pat_t; -static Text_t replace_array(Text_t text, Array_t replacements, Text_t backref_pat, bool recursive); +static Text_t replace_list(Text_t text, List_t replacements, Text_t backref_pat, bool recursive); static INLINE void skip_whitespace(TextIter_t *state, int64_t *i) { @@ -821,15 +821,15 @@ static OptionalPatternMatch find(Text_t text, Text_t pattern, Int_t from_index) if (found == -1) return NONE_MATCH; - Array_t capture_array = {}; + List_t capture_list = {}; for (int i = 0; captures[i].occupied; i++) { Text_t capture = Text$slice(text, I(captures[i].index+1), I(captures[i].index+captures[i].length)); - Array$insert(&capture_array, &capture, I(0), sizeof(Text_t)); + List$insert(&capture_list, &capture, I(0), sizeof(Text_t)); } return (OptionalPatternMatch){ .text=Text$slice(text, I(found+1), I(found+len)), .index=I(found+1), - .captures=capture_array, + .captures=capture_list, }; } @@ -858,33 +858,33 @@ static bool Pattern$matches(Text_t text, Text_t pattern) return (match_len == text.length); } -static OptionalArray_t Pattern$captures(Text_t text, Text_t pattern) +static OptionalList_t Pattern$captures(Text_t text, Text_t pattern) { capture_t captures[MAX_BACKREFS] = {}; int64_t match_len = match(text, 0, pattern, 0, captures, 0); if (match_len != text.length) - return NONE_ARRAY; + return NONE_LIST; - Array_t capture_array = {}; + List_t capture_list = {}; for (int i = 0; captures[i].occupied; i++) { Text_t capture = Text$slice(text, I(captures[i].index+1), I(captures[i].index+captures[i].length)); - Array$insert(&capture_array, &capture, I(0), sizeof(Text_t)); + List$insert(&capture_list, &capture, I(0), sizeof(Text_t)); } - return capture_array; + return capture_list; } -static Array_t Pattern$find_all(Text_t text, Text_t pattern) +static List_t Pattern$find_all(Text_t text, Text_t pattern) { if (pattern.length == 0) // special case - return (Array_t){.length=0}; + return (List_t){.length=0}; - Array_t matches = {}; + List_t matches = {}; for (int64_t i = 1; ; ) { OptionalPatternMatch m = find(text, pattern, I(i)); if (m.is_none) break; i = Int64$from_int(m.index, false) + m.text.length; - Array$insert(&matches, &m, I_small(0), sizeof(PatternMatch)); + List$insert(&matches, &m, I_small(0), sizeof(PatternMatch)); } return matches; } @@ -916,7 +916,7 @@ static Closure_t Pattern$by_match(Text_t text, Text_t pattern) }; } -static Text_t apply_backrefs(Text_t text, Array_t recursive_replacements, Text_t replacement, Text_t backref_pat, capture_t *captures) +static Text_t apply_backrefs(Text_t text, List_t recursive_replacements, Text_t replacement, Text_t backref_pat, capture_t *captures) { if (backref_pat.length == 0) return replacement; @@ -960,7 +960,7 @@ static Text_t apply_backrefs(Text_t text, Array_t recursive_replacements, Text_t Text_t backref_text = Text$slice(text, I(captures[backref].index+1), I(captures[backref].index + captures[backref].length)); if (captures[backref].recursive && recursive_replacements.length > 0) - backref_text = replace_array(backref_text, recursive_replacements, backref_pat, true); + backref_text = replace_list(backref_text, recursive_replacements, backref_pat, true); if (pos > nonmatching_pos) { Text_t before_slice = Text$slice(replacement, I(nonmatching_pos+1), I(pos)); @@ -989,7 +989,7 @@ static Text_t Pattern$replace(Text_t text, Text_t pattern, Text_t replacement, T && !uc_is_property((ucs4_t)first_grapheme, UC_PROPERTY_PAIRED_PUNCTUATION)); Text_t entries[2] = {pattern, replacement}; - Array_t replacements = { + List_t replacements = { .data=entries, .length=1, .stride=sizeof(entries), @@ -1015,7 +1015,7 @@ static Text_t Pattern$replace(Text_t text, Text_t pattern, Text_t replacement, T .occupied = true, .recursive = false, }; - Text_t replacement_text = apply_backrefs(text, recursive ? replacements : (Array_t){}, replacement, backref_pat, captures); + Text_t replacement_text = apply_backrefs(text, recursive ? replacements : (List_t){}, replacement, backref_pat, captures); if (pos > nonmatching_pos) { Text_t before_slice = Text$slice(text, I(nonmatching_pos+1), I(pos)); ret = Text$concat(ret, before_slice, replacement_text); @@ -1084,7 +1084,7 @@ static Text_t Pattern$map(Text_t text, Text_t pattern, Closure_t fn, bool recurs Text_t capture = Text$slice(text, I(captures[i].index+1), I(captures[i].index+captures[i].length)); if (recursive) capture = Pattern$map(capture, pattern, fn, recursive); - Array$insert(&m.captures, &capture, I(0), sizeof(Text_t)); + List$insert(&m.captures, &capture, I(0), sizeof(Text_t)); } Text_t replacement = text_mapper(m, fn.userdata); @@ -1133,7 +1133,7 @@ static void Pattern$each(Text_t text, Text_t pattern, Closure_t fn, bool recursi Text_t capture = Text$slice(text, I(captures[i].index+1), I(captures[i].index+captures[i].length)); if (recursive) Pattern$each(capture, pattern, fn, recursive); - Array$insert(&m.captures, &capture, I(0), sizeof(Text_t)); + List$insert(&m.captures, &capture, I(0), sizeof(Text_t)); } action(m, fn.userdata); @@ -1141,7 +1141,7 @@ static void Pattern$each(Text_t text, Text_t pattern, Closure_t fn, bool recursi } } -Text_t replace_array(Text_t text, Array_t replacements, Text_t backref_pat, bool recursive) +Text_t replace_list(Text_t text, List_t replacements, Text_t backref_pat, bool recursive) { if (replacements.length == 0) return text; @@ -1166,7 +1166,7 @@ Text_t replace_array(Text_t text, Array_t replacements, Text_t backref_pat, bool // Concatenate the replacement: Text_t replacement = *(Text_t*)(replacements.data + i*replacements.stride + sizeof(Text_t)); - Text_t replacement_text = apply_backrefs(text, recursive ? replacements : (Array_t){}, replacement, backref_pat, captures); + Text_t replacement_text = apply_backrefs(text, recursive ? replacements : (List_t){}, replacement, backref_pat, captures); ret = Text$concat(ret, replacement_text); pos += MAX(len, 1); nonmatch_pos = pos; @@ -1187,18 +1187,18 @@ Text_t replace_array(Text_t text, Array_t replacements, Text_t backref_pat, bool static Text_t Pattern$replace_all(Text_t text, Table_t replacements, Text_t backref_pat, bool recursive) { - return replace_array(text, replacements.entries, backref_pat, recursive); + return replace_list(text, replacements.entries, backref_pat, recursive); } -static Array_t Pattern$split(Text_t text, Text_t pattern) +static List_t Pattern$split(Text_t text, Text_t pattern) { if (text.length == 0) // special case - return (Array_t){.length=0}; + return (List_t){.length=0}; if (pattern.length == 0) // special case return Text$clusters(text); - Array_t chunks = {}; + List_t chunks = {}; int64_t i = 0; for (;;) { @@ -1208,12 +1208,12 @@ static Array_t Pattern$split(Text_t text, Text_t pattern) found = _find(text, pattern, i + 1, text.length-1, &len, NULL); if (found < 0) break; Text_t chunk = Text$slice(text, I(i+1), I(found)); - Array$insert(&chunks, &chunk, I_small(0), sizeof(Text_t)); + List$insert(&chunks, &chunk, I_small(0), sizeof(Text_t)); i = MAX(found + len, i + 1); } Text_t last_chunk = Text$slice(text, I(i+1), I(text.length)); - Array$insert(&chunks, &last_chunk, I_small(0), sizeof(Text_t)); + List$insert(&chunks, &last_chunk, I_small(0), sizeof(Text_t)); return chunks; } diff --git a/examples/random/README.md b/examples/random/README.md index d9983ab7..183b9d0b 100644 --- a/examples/random/README.md +++ b/examples/random/README.md @@ -16,8 +16,8 @@ a Tomo program launches. ## RNG Functions This documentation provides details on RNG functions available in the API. -Arrays also have some methods which use RNG values: -`array.shuffle()`, `array.shuffled()`, `array.random()`, and `array.sample()`. +Lists also have some methods which use RNG values: +`list.shuffle()`, `list.shuffled()`, `list.random()`, and `list.sample()`. - [`func bool(rng: RNG, p: Num = 0.5 -> Bool)`](#bool) - [`func byte(rng: RNG -> Byte)`](#byte) @@ -75,7 +75,7 @@ A random byte (0-255). --- ### `bytes` -Generate an array of uniformly random bytes with the given length. +Generate a list of uniformly random bytes with the given length. ```tomo func bytes(rng: RNG, count: Int -> [Byte]) @@ -85,7 +85,7 @@ func bytes(rng: RNG, count: Int -> [Byte]) - `count`: The number of random bytes to return. **Returns:** -An array of length `count` random bytes with uniform random distribution (0-255). +A list of length `count` random bytes with uniform random distribution (0-255). **Example:** ```tomo diff --git a/examples/random/random.tm b/examples/random/random.tm index 13600b47..14b68d7f 100644 --- a/examples/random/random.tm +++ b/examples/random/random.tm @@ -21,7 +21,7 @@ func _os_random_bytes(count:Int64 -> [Byte]) return C_code:[Byte]( uint8_t *random_bytes = GC_MALLOC_ATOMIC(@count); getrandom(random_bytes, @count, 0); - (Array_t){.length=@count, .data=random_bytes, .stride=1, .atomic=1} + (List_t){.length=@count, .data=random_bytes, .stride=1, .atomic=1} ) struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret) func new(seed:[Byte]?=none, -> @RandomNumberGenerator) @@ -36,7 +36,7 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret // Immediately reinitialize for backtracking resistance chacha_keysetup(&@rng->_chacha, new_keystream); chacha_ivsetup(&@rng->_chacha, new_keystream + KEYSZ); - Array_t new_bytes = (Array_t){.data=GC_MALLOC_ATOMIC(1024), .length=1024, .stride=1, .atomic=1}; + List_t new_bytes = (List_t){.data=GC_MALLOC_ATOMIC(1024), .length=1024, .stride=1, .atomic=1}; memset(new_bytes.data, 0, new_bytes.length); chacha_encrypt_bytes(&@rng->_chacha, new_bytes.data, new_bytes.data, new_bytes.length); new_bytes @@ -65,7 +65,7 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret count64 := Int64(count) buf := C_code:@Memory(GC_MALLOC_ATOMIC(@count64)) rng._fill_bytes(buf, count64) - return C_code:[Byte]((Array_t){.data=@buf, .stride=1, .atomic=1, .length=@count64}) + return C_code:[Byte]((List_t){.data=@buf, .stride=1, .atomic=1, .length=@count64}) func byte(rng:&RandomNumberGenerator -> Byte) byte : &Byte @@ -149,7 +149,7 @@ CORD ast_to_xml(ast_t *ast) T(StackReference, "<StackReference>%r</StackReference>", ast_to_xml(data.value)) T(Min, "<Min>%r%r%r</Min>", ast_to_xml(data.lhs), ast_to_xml(data.rhs), optional_tagged("key", data.key)) T(Max, "<Max>%r%r%r</Max>", ast_to_xml(data.lhs), ast_to_xml(data.rhs), optional_tagged("key", data.key)) - T(Array, "<Array>%r</Array>", ast_list_to_xml(data.items)) + T(List, "<List>%r</List>", ast_list_to_xml(data.items)) T(Set, "<Set>%r</Set>", ast_list_to_xml(data.items)) T(Table, "<Table>%r%r</Table>", optional_tagged("default-value", data.default_value), @@ -224,7 +224,7 @@ CORD type_ast_to_xml(type_ast_t *t) T(VarTypeAST, "%s", data.name) T(PointerTypeAST, "<PointerType is_stack=\"%s\">%r</PointerType>", data.is_stack ? "yes" : "no", type_ast_to_xml(data.pointed)) - T(ArrayTypeAST, "<ArrayType>%r</ArrayType>", type_ast_to_xml(data.item)) + T(ListTypeAST, "<ListType>%r</ListType>", type_ast_to_xml(data.item)) T(SetTypeAST, "<TableType>%r</TableType>", type_ast_to_xml(data.item)) T(TableTypeAST, "<TableType>%r %r</TableType>", type_ast_to_xml(data.key), type_ast_to_xml(data.value)) T(FunctionTypeAST, "<FunctionType>%r %r</FunctionType>", arg_list_to_xml(data.args), type_ast_to_xml(data.ret)) @@ -64,7 +64,7 @@ typedef enum { UnknownTypeAST, VarTypeAST, PointerTypeAST, - ArrayTypeAST, + ListTypeAST, SetTypeAST, TableTypeAST, FunctionTypeAST, @@ -93,7 +93,7 @@ struct type_ast_s { } PointerTypeAST; struct { type_ast_t *item; - } ArrayTypeAST; + } ListTypeAST; struct { type_ast_t *key, *value; ast_t *default_value; @@ -134,7 +134,7 @@ typedef enum { RightShiftUpdate, UnsignedRightShiftUpdate, AndUpdate, OrUpdate, XorUpdate, Not, Negative, HeapAllocate, StackReference, Min, Max, - Array, Set, Table, TableEntry, Comprehension, + List, Set, Table, TableEntry, Comprehension, FunctionDef, Lambda, ConvertDef, FunctionCall, MethodCall, Block, @@ -204,7 +204,7 @@ struct ast_s { } Min, Max; struct { ast_list_t *items; - } Array; + } List; struct { ast_list_t *items; } Set; diff --git a/src/compile.c b/src/compile.c index cdfaf5c6..6cb25de0 100644 --- a/src/compile.c +++ b/src/compile.c @@ -34,14 +34,14 @@ static CORD compile_none(type_t *t); static CORD compile_empty(type_t *t); static CORD compile_declared_value(env_t *env, ast_t *declaration_ast); static CORD compile_to_type(env_t *env, ast_t *ast, type_t *t); -static CORD compile_typed_array(env_t *env, ast_t *ast, type_t *array_type); +static CORD compile_typed_list(env_t *env, ast_t *ast, type_t *list_type); static CORD compile_typed_set(env_t *env, ast_t *ast, type_t *set_type); static CORD compile_typed_table(env_t *env, ast_t *ast, type_t *table_type); static CORD compile_typed_allocation(env_t *env, ast_t *ast, type_t *pointer_type); static CORD check_none(type_t *t, CORD value); static CORD optional_into_nonnone(type_t *t, CORD value); static CORD compile_string_literal(CORD literal); -static ast_t *add_to_array_comprehension(ast_t *item, ast_t *subject); +static ast_t *add_to_list_comprehension(ast_t *item, ast_t *subject); static ast_t *add_to_table_comprehension(ast_t *entry, ast_t *subject); static ast_t *add_to_set_comprehension(ast_t *item, ast_t *subject); static CORD compile_lvalue(env_t *env, ast_t *ast); @@ -174,9 +174,9 @@ static bool promote(env_t *env, ast_t *ast, CORD *code, type_t *actual, type_t * return true; } - // Set -> Array promotion: - if (needed->tag == ArrayType && actual->tag == SetType - && type_eq(Match(needed, ArrayType)->item_type, Match(actual, SetType)->item_type)) { + // Set -> List promotion: + if (needed->tag == ListType && actual->tag == SetType + && type_eq(Match(needed, ListType)->item_type, Match(actual, SetType)->item_type)) { *code = CORD_all("(", *code, ").entries"); return true; } @@ -187,8 +187,8 @@ static bool promote(env_t *env, ast_t *ast, CORD *code, type_t *actual, type_t * CORD compile_maybe_incref(env_t *env, ast_t *ast, type_t *t) { if (is_idempotent(ast) && can_be_mutated(env, ast)) { - if (t->tag == ArrayType) - return CORD_all("ARRAY_COPY(", compile_to_type(env, ast, t), ")"); + if (t->tag == ListType) + return CORD_all("LIST_COPY(", compile_to_type(env, ast, t), ")"); else if (t->tag == TableType || t->tag == SetType) return CORD_all("TABLE_COPY(", compile_to_type(env, ast, t), ")"); } @@ -253,8 +253,8 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, Max)->key); break; } - case Array: { - for (ast_list_t *item = Match(ast, Array)->items; item; item = item->next) + case List: { + for (ast_list_t *item = Match(ast, List)->items; item; item = item->next) add_closed_vars(closed_vars, enclosing_scope, env, item->ast); break; } @@ -283,7 +283,7 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t return add_closed_vars(closed_vars, enclosing_scope, env, loop); } - // Array/Set/Table comprehension: + // List/Set/Table comprehension: ast_t *body = comp->expr; if (comp->filter) body = WrapAST(comp->expr, If, .condition=comp->filter, .body=body); @@ -746,8 +746,8 @@ static CORD compile_binary_op(env_t *env, ast_t *ast) case TextType: { return CORD_all("Text$concat(", lhs, ", ", rhs, ")"); } - case ArrayType: { - return CORD_all("Array$concat(", lhs, ", ", rhs, ", sizeof(", compile_type(Match(overall_t, ArrayType)->item_type), "))"); + case ListType: { + return CORD_all("List$concat(", lhs, ", ", rhs, ", sizeof(", compile_type(Match(overall_t, ListType)->item_type), "))"); } default: code_err(ast, "Concatenation isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), " values"); @@ -793,7 +793,7 @@ CORD compile_type(type_t *t) else return CORD_all(namespace_prefix(text->env, text->env->namespace->parent), text->lang, "$$type"); } - case ArrayType: return "Array_t"; + case ListType: return "List_t"; case SetType: return "Table_t"; case TableType: return "Table_t"; case FunctionType: { @@ -827,7 +827,7 @@ CORD compile_type(type_t *t) case TextType: return Match(nonnull, TextType)->lang ? compile_type(nonnull) : "OptionalText_t"; case IntType: case BigIntType: case NumType: case BoolType: case ByteType: - case ArrayType: case TableType: case SetType: + case ListType: case TableType: case SetType: return CORD_all("Optional", compile_type(nonnull)); case StructType: { if (nonnull == PATH_TYPE) @@ -872,19 +872,19 @@ CORD compile_lvalue(env_t *env, ast_t *ast) container_t = value_type(container_t); type_t *index_t = get_type(env, index->index); - if (container_t->tag == ArrayType) { + if (container_t->tag == ListType) { CORD target_code = compile_to_pointer_depth(env, index->indexed, 1, false); - type_t *item_type = Match(container_t, ArrayType)->item_type; + type_t *item_type = Match(container_t, ListType)->item_type; CORD index_code = index->index->tag == Int ? compile_int_to_type(env, index->index, Type(IntType, .bits=TYPE_IBITS64)) : (index_t->tag == BigIntType ? CORD_all("Int64$from_int(", compile(env, index->index), ", no)") : CORD_all("(Int64_t)(", compile(env, index->index), ")")); if (index->unchecked) { - return CORD_all("Array_lvalue_unchecked(", compile_type(item_type), ", ", target_code, ", ", + return CORD_all("List_lvalue_unchecked(", compile_type(item_type), ", ", target_code, ", ", index_code, ", sizeof(", compile_type(item_type), "))"); } else { - return CORD_all("Array_lvalue(", compile_type(item_type), ", ", target_code, ", ", + return CORD_all("List_lvalue(", compile_type(item_type), ", ", target_code, ", ", index_code, ", ", String((int)(ast->start - ast->file->text)), ", ", String((int)(ast->end - ast->file->text)), ")"); @@ -975,7 +975,7 @@ CORD check_none(type_t *t, CORD value) return CORD_all("({(", value, ").fn == NULL;})"); else if (t->tag == NumType) return CORD_all("isnan(", value, ")"); - else if (t->tag == ArrayType) + else if (t->tag == ListType) return CORD_all("({(", value, ").length < 0;})"); else if (t->tag == TableType || t->tag == SetType) return CORD_all("({(", value, ").entries.length < 0;})"); @@ -997,7 +997,7 @@ static CORD compile_condition(env_t *env, ast_t *ast) return compile(env, ast); } else if (t->tag == TextType) { return CORD_all("(", compile(env, ast), ").length"); - } else if (t->tag == ArrayType) { + } else if (t->tag == ListType) { return CORD_all("(", compile(env, ast), ").length"); } else if (t->tag == TableType || t->tag == SetType) { return CORD_all("(", compile(env, ast), ").entries.length"); @@ -1462,7 +1462,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast) auto for_ = Match(ast, For); // If we're iterating over a comprehension, that's actually just doing - // one loop, we don't need to compile the comprehension as an array + // one loop, we don't need to compile the comprehension as a list // comprehension. This is a common case for reducers like `(+: i*2 for i in 5)` // or `(and) x.is_good() for x in xs` if (for_->iter->tag == Comprehension) { @@ -1565,8 +1565,8 @@ static CORD _compile_statement(env_t *env, ast_t *ast) type_t *iter_value_t = value_type(iter_t); switch (iter_value_t->tag) { - case ArrayType: { - type_t *item_t = Match(iter_value_t, ArrayType)->item_type; + case ListType: { + type_t *item_t = Match(iter_value_t, ListType)->item_type; CORD index = CORD_EMPTY; CORD value = CORD_EMPTY; if (for_->vars) { @@ -1601,17 +1601,17 @@ static CORD _compile_statement(env_t *env, ast_t *ast) if (iter_t->tag == PointerType) { loop = CORD_all("{\n" - "Array_t *ptr = ", compile_to_pointer_depth(env, for_->iter, 1, false), ";\n" - "\nARRAY_INCREF(*ptr);\n" - "Array_t iterating = *ptr;\n", + "List_t *ptr = ", compile_to_pointer_depth(env, for_->iter, 1, false), ";\n" + "\nLIST_INCREF(*ptr);\n" + "List_t iterating = *ptr;\n", loop, stop, - "\nARRAY_DECREF(*ptr);\n" + "\nLIST_DECREF(*ptr);\n" "}\n"); } else { loop = CORD_all("{\n" - "Array_t iterating = ", compile_to_pointer_depth(env, for_->iter, 0, false), ";\n", + "List_t iterating = ", compile_to_pointer_depth(env, for_->iter, 0, false), ";\n", loop, stop, "}\n"); @@ -1657,15 +1657,15 @@ static CORD _compile_statement(env_t *env, ast_t *ast) loop = CORD_all( "{\n", "Table_t *t = ", compile_to_pointer_depth(env, for_->iter, 1, false), ";\n" - "ARRAY_INCREF(t->entries);\n" - "Array_t iterating = t->entries;\n", + "LIST_INCREF(t->entries);\n" + "List_t iterating = t->entries;\n", loop, - "ARRAY_DECREF(t->entries);\n" + "LIST_DECREF(t->entries);\n" "}\n"); } else { loop = CORD_all( "{\n", - "Array_t iterating = (", compile_to_pointer_depth(env, for_->iter, 0, false), ").entries;\n", + "List_t iterating = (", compile_to_pointer_depth(env, for_->iter, 0, false), ").entries;\n", loop, "}\n"); } @@ -1834,7 +1834,7 @@ static CORD _compile_statement(env_t *env, ast_t *ast) return compile_statement(env, loop); } - // Array/Set/Table comprehension: + // List/Set/Table comprehension: comprehension_body_t get_body = (void*)env->comprehension_action->fn; ast_t *body = get_body(comp->expr, env->comprehension_action->userdata); if (comp->filter) @@ -1911,7 +1911,7 @@ CORD expr_as_text(CORD expr, type_t *t, CORD color) return CORD_asprintf("%r$as_text(stack(%r), %r, &%r$info)", name, expr, color, name); } case TextType: return CORD_asprintf("Text$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(t)); - case ArrayType: return CORD_asprintf("Array$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(t)); + case ListType: return CORD_asprintf("List$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(t)); case SetType: return CORD_asprintf("Table$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(t)); case TableType: return CORD_asprintf("Table$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(t)); case FunctionType: case ClosureType: return CORD_asprintf("Func$as_text(stack(%r), %r, %r)", expr, color, compile_type_info(t)); @@ -1964,8 +1964,8 @@ CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool t = ptr->pointed; } - if (needs_incref && t->tag == ArrayType) - val = CORD_all("ARRAY_COPY(", val, ")"); + if (needs_incref && t->tag == ListType) + val = CORD_all("LIST_COPY(", val, ")"); else if (needs_incref && (t->tag == TableType || t->tag == SetType)) val = CORD_all("TABLE_COPY(", val, ")"); @@ -1991,8 +1991,8 @@ CORD compile_to_type(env_t *env, ast_t *ast, type_t *t) return compile_none(t); } else if (t->tag == PointerType && (ast->tag == HeapAllocate || ast->tag == StackReference)) { return compile_typed_allocation(env, ast, t); - } else if (t->tag == ArrayType && ast->tag == Array) { - return compile_typed_array(env, ast, t); + } else if (t->tag == ListType && ast->tag == List) { + return compile_typed_list(env, ast, t); } else if (t->tag == TableType && ast->tag == Table) { return compile_typed_table(env, ast, t); } else if (t->tag == SetType && ast->tag == Set) { @@ -2015,7 +2015,7 @@ CORD compile_to_type(env_t *env, ast_t *ast, type_t *t) type_t *self_type = get_type(env, methodcall->self); // Currently, this is only implemented for cases where you have the return type // and the self type equal to each other, because that's the main case I care - // about with array and set methods (e.g. `Array.sorted()`) + // about with list and set methods (e.g. `List.sorted()`) if (is_incomplete_type(self_type) && type_eq(self_type, actual)) { type_t *completed_self = most_complete_type(self_type, t); if (completed_self) { @@ -2037,48 +2037,48 @@ CORD compile_to_type(env_t *env, ast_t *ast, type_t *t) return code; } -CORD compile_typed_array(env_t *env, ast_t *ast, type_t *array_type) +CORD compile_typed_list(env_t *env, ast_t *ast, type_t *list_type) { - auto array = Match(ast, Array); - if (!array->items) - return "(Array_t){.length=0}"; + auto list = Match(ast, List); + if (!list->items) + return "(List_t){.length=0}"; - type_t *item_type = Match(array_type, ArrayType)->item_type; + type_t *item_type = Match(list_type, ListType)->item_type; int64_t n = 0; - for (ast_list_t *item = array->items; item; item = item->next) { + for (ast_list_t *item = list->items; item; item = item->next) { ++n; if (item->ast->tag == Comprehension) - goto array_comprehension; + goto list_comprehension; } { env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : env; if (is_incomplete_type(item_type)) - code_err(ast, "This array's type can't be inferred!"); - CORD code = CORD_all("TypedArrayN(", compile_type(item_type), CORD_asprintf(", %ld", n)); - for (ast_list_t *item = array->items; item; item = item->next) { + code_err(ast, "This list's type can't be inferred!"); + CORD code = CORD_all("TypedListN(", compile_type(item_type), CORD_asprintf(", %ld", n)); + for (ast_list_t *item = list->items; item; item = item->next) { code = CORD_all(code, ", ", compile_to_type(scope, item->ast, item_type)); } return CORD_cat(code, ")"); } - array_comprehension: + list_comprehension: { env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env); static int64_t comp_num = 1; - const char *comprehension_name = String("arr$", comp_num++); + const char *comprehension_name = String("list$", comp_num++); ast_t *comprehension_var = LiteralCode(CORD_all("&", comprehension_name), - .type=Type(PointerType, .pointed=array_type, .is_stack=true)); - Closure_t comp_action = {.fn=add_to_array_comprehension, .userdata=comprehension_var}; + .type=Type(PointerType, .pointed=list_type, .is_stack=true)); + Closure_t comp_action = {.fn=add_to_list_comprehension, .userdata=comprehension_var}; scope->comprehension_action = &comp_action; - CORD code = CORD_all("({ Array_t ", comprehension_name, " = {};"); - // set_binding(scope, comprehension_name, array_type, comprehension_name); - for (ast_list_t *item = array->items; item; item = item->next) { + CORD code = CORD_all("({ List_t ", comprehension_name, " = {};"); + // set_binding(scope, comprehension_name, list_type, comprehension_name); + for (ast_list_t *item = list->items; item; item = item->next) { if (item->ast->tag == Comprehension) code = CORD_all(code, "\n", compile_statement(scope, item->ast)); else - code = CORD_all(code, compile_statement(env, add_to_array_comprehension(item->ast, comprehension_var))); + code = CORD_all(code, compile_statement(env, add_to_list_comprehension(item->ast, comprehension_var))); } code = CORD_all(code, " ", comprehension_name, "; })"); return code; @@ -2463,7 +2463,7 @@ CORD compile_none(type_t *t) } case BoolType: return "NONE_BOOL"; case ByteType: return "NONE_BYTE"; - case ArrayType: return "NONE_ARRAY"; + case ListType: return "NONE_LIST"; case TableType: return "NONE_TABLE"; case SetType: return "NONE_TABLE"; case TextType: return "NONE_TEXT"; @@ -2505,7 +2505,7 @@ CORD compile_empty(type_t *t) } case ByteType: return "((Byte_t)0)"; case BoolType: return "((Bool_t)no)"; - case ArrayType: return "((Array_t){})"; + case ListType: return "((List_t){})"; case TableType: case SetType: return "((Table_t){})"; case TextType: return "Text(\"\")"; case CStringType: return "\"\""; @@ -2577,7 +2577,7 @@ ast_t *add_to_table_comprehension(ast_t *entry, ast_t *subject) .args=new(arg_ast_t, .value=e->key, .next=new(arg_ast_t, .value=e->value))); } -ast_t *add_to_array_comprehension(ast_t *item, ast_t *subject) +ast_t *add_to_list_comprehension(ast_t *item, ast_t *subject) { return WrapAST(item, MethodCall, .name="insert", .self=subject, .args=new(arg_ast_t, .value=item)); } @@ -2634,7 +2634,7 @@ CORD compile(env_t *env, ast_t *ast) return CORD_all("!(", compile(env, value), ")"); else if (t->tag == IntType || t->tag == ByteType) return CORD_all("~(", compile(env, value), ")"); - else if (t->tag == ArrayType) + else if (t->tag == ListType) return CORD_all("((", compile(env, value), ").length == 0)"); else if (t->tag == SetType || t->tag == TableType) return CORD_all("((", compile(env, value), ").entries.length == 0)"); @@ -2884,13 +2884,13 @@ CORD compile(env_t *env, ast_t *ast) comparison, " ? ternary$lhs : ternary$rhs;\n" "})"); } - case Array: { - auto array = Match(ast, Array); - if (!array->items) - return "(Array_t){.length=0}"; + case List: { + auto list = Match(ast, List); + if (!list->items) + return "(List_t){.length=0}"; - type_t *array_type = get_type(env, ast); - return compile_typed_array(env, ast, array_type); + type_t *list_type = get_type(env, ast); + return compile_typed_list(env, ast, list_type); } case Table: { auto table = Match(ast, Table); @@ -2919,7 +2919,7 @@ CORD compile(env_t *env, ast_t *ast) if (base->tag == TableEntry) return compile(env, WrapAST(ast, Table, .entries=new(ast_list_t, .ast=ast))); else - return compile(env, WrapAST(ast, Array, .items=new(ast_list_t, .ast=ast))); + return compile(env, WrapAST(ast, List, .items=new(ast_list_t, .ast=ast))); } case Lambda: { auto lambda = Match(ast, Lambda); @@ -2989,8 +2989,8 @@ CORD compile(env_t *env, ast_t *ast) binding_t *b = get_binding(env, entry->name); assert(b); CORD binding_code = b->code; - if (entry->b->type->tag == ArrayType) - userdata = CORD_all(userdata, ", ARRAY_COPY(", binding_code, ")"); + if (entry->b->type->tag == ListType) + userdata = CORD_all(userdata, ", LIST_COPY(", binding_code, ")"); else if (entry->b->type->tag == TableType || entry->b->type->tag == SetType) userdata = CORD_all(userdata, ", TABLE_COPY(", binding_code, ")"); else @@ -3049,66 +3049,66 @@ CORD compile(env_t *env, ast_t *ast) else if (pointer_depth > 1) code_err(call->self, "I expected "article" "name" pointer here, not a nested "name" pointer"); \ } while (0) switch (self_value_t->tag) { - case ArrayType: { - type_t *item_t = Match(self_value_t, ArrayType)->item_type; + case ListType: { + type_t *item_t = Match(self_value_t, ListType)->item_type; CORD padded_item_size = CORD_all("sizeof(", compile_type(item_t), ")"); if (streq(call->name, "insert")) { - EXPECT_POINTER("an", "array"); + EXPECT_POINTER("a", "list"); arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, .next=new(arg_t, .name="at", .type=INT_TYPE, .default_val=FakeAST(Int, .str="0"))); - return CORD_all("Array$insert_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + return CORD_all("List$insert_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); } else if (streq(call->name, "insert_all")) { - EXPECT_POINTER("an", "array"); + EXPECT_POINTER("a", "list"); arg_t *arg_spec = new(arg_t, .name="items", .type=self_value_t, .next=new(arg_t, .name="at", .type=INT_TYPE, .default_val=FakeAST(Int, .str="0"))); - return CORD_all("Array$insert_all(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + return CORD_all("List$insert_all(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); } else if (streq(call->name, "remove_at")) { - EXPECT_POINTER("an", "array"); + EXPECT_POINTER("a", "list"); arg_t *arg_spec = new(arg_t, .name="index", .type=INT_TYPE, .default_val=FakeAST(Int, .str="-1"), .next=new(arg_t, .name="count", .type=INT_TYPE, .default_val=FakeAST(Int, .str="1"))); - return CORD_all("Array$remove_at(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + return CORD_all("List$remove_at(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); } else if (streq(call->name, "remove_item")) { - EXPECT_POINTER("an", "array"); + EXPECT_POINTER("a", "list"); arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, .next=new(arg_t, .name="max_count", .type=INT_TYPE, .default_val=FakeAST(Int, .str="-1"))); - return CORD_all("Array$remove_item_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + return CORD_all("List$remove_item_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type_info(self_value_t), ")"); } else if (streq(call->name, "has")) { self = compile_to_pointer_depth(env, call->self, 0, false); arg_t *arg_spec = new(arg_t, .name="item", .type=item_t); - return CORD_all("Array$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + return CORD_all("List$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type_info(self_value_t), ")"); } else if (streq(call->name, "sample")) { type_t *random_num_type = parse_type_string(env, "func(->Num)?"); self = compile_to_pointer_depth(env, call->self, 0, false); arg_t *arg_spec = new(arg_t, .name="count", .type=INT_TYPE, - .next=new(arg_t, .name="weights", .type=Type(ArrayType, .item_type=Type(NumType, .bits=TYPE_NBITS64)), + .next=new(arg_t, .name="weights", .type=Type(ListType, .item_type=Type(NumType, .bits=TYPE_NBITS64)), .default_val=FakeAST(None), .next=new(arg_t, .name="random", .type=random_num_type, .default_val=FakeAST(None)))); - return CORD_all("Array$sample(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + return CORD_all("List$sample(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); } else if (streq(call->name, "shuffle")) { type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?"); - EXPECT_POINTER("an", "array"); + EXPECT_POINTER("a", "list"); arg_t *arg_spec = new(arg_t, .name="random", .type=random_int64_type, .default_val=FakeAST(None)); - return CORD_all("Array$shuffle(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); + return CORD_all("List$shuffle(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); } else if (streq(call->name, "shuffled")) { type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?"); self = compile_to_pointer_depth(env, call->self, 0, false); arg_t *arg_spec = new(arg_t, .name="random", .type=random_int64_type, .default_val=FakeAST(None)); - return CORD_all("Array$shuffled(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); + return CORD_all("List$shuffled(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); } else if (streq(call->name, "random")) { type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?"); self = compile_to_pointer_depth(env, call->self, 0, false); arg_t *arg_spec = new(arg_t, .name="random", .type=random_int64_type, .default_val=FakeAST(None)); - return CORD_all("Array$random_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type(item_t), ")"); + return CORD_all("List$random_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type(item_t), ")"); } else if (streq(call->name, "sort") || streq(call->name, "sorted")) { if (streq(call->name, "sort")) - EXPECT_POINTER("an", "array"); + EXPECT_POINTER("a", "list"); else self = compile_to_pointer_depth(env, call->self, 0, false); CORD comparison; @@ -3120,9 +3120,9 @@ CORD compile(env_t *env, ast_t *ast) } else { comparison = CORD_all("((Closure_t){.fn=generic_compare, .userdata=(void*)", compile_type_info(item_t), "})"); } - return CORD_all("Array$", call->name, "(", self, ", ", comparison, ", ", padded_item_size, ")"); + return CORD_all("List$", call->name, "(", self, ", ", comparison, ", ", padded_item_size, ")"); } else if (streq(call->name, "heapify")) { - EXPECT_POINTER("an", "array"); + EXPECT_POINTER("a", "list"); CORD comparison; if (call->args) { type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); @@ -3132,9 +3132,9 @@ CORD compile(env_t *env, ast_t *ast) } else { comparison = CORD_all("((Closure_t){.fn=generic_compare, .userdata=(void*)", compile_type_info(item_t), "})"); } - return CORD_all("Array$heapify(", self, ", ", comparison, ", ", padded_item_size, ")"); + return CORD_all("List$heapify(", self, ", ", comparison, ", ", padded_item_size, ")"); } else if (streq(call->name, "heap_push")) { - EXPECT_POINTER("an", "array"); + EXPECT_POINTER("a", "list"); type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); type_t *fn_t = NewFunctionType(Type(IntType, .bits=TYPE_IBITS32), {.name="x", .type=item_ptr}, {.name="y", .type=item_ptr}); ast_t *default_cmp = LiteralCode(CORD_all("((Closure_t){.fn=generic_compare, .userdata=(void*)", @@ -3143,9 +3143,9 @@ CORD compile(env_t *env, ast_t *ast) arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, .next=new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t), .default_val=default_cmp)); CORD arg_code = compile_arguments(env, ast, arg_spec, call->args); - return CORD_all("Array$heap_push_value(", self, ", ", arg_code, ", ", padded_item_size, ")"); + return CORD_all("List$heap_push_value(", self, ", ", arg_code, ", ", padded_item_size, ")"); } else if (streq(call->name, "heap_pop")) { - EXPECT_POINTER("an", "array"); + EXPECT_POINTER("a", "list"); type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); type_t *fn_t = NewFunctionType(Type(IntType, .bits=TYPE_IBITS32), {.name="x", .type=item_ptr}, {.name="y", .type=item_ptr}); ast_t *default_cmp = LiteralCode(CORD_all("((Closure_t){.fn=generic_compare, .userdata=(void*)", @@ -3153,7 +3153,7 @@ CORD compile(env_t *env, ast_t *ast) .type=Type(ClosureType, .fn=fn_t)); arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t), .default_val=default_cmp); CORD arg_code = compile_arguments(env, ast, arg_spec, call->args); - return CORD_all("Array$heap_pop_value(", self, ", ", arg_code, ", ", compile_type(item_t), ", _, ", + return CORD_all("List$heap_pop_value(", self, ", ", arg_code, ", ", compile_type(item_t), ", _, ", promote_to_optional(item_t, "_"), ", ", compile_none(item_t), ")"); } else if (streq(call->name, "binary_search")) { self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); @@ -3166,15 +3166,15 @@ CORD compile(env_t *env, ast_t *ast) arg_t *arg_spec = new(arg_t, .name="target", .type=item_t, .next=new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t), .default_val=default_cmp)); CORD arg_code = compile_arguments(env, ast, arg_spec, call->args); - return CORD_all("Array$binary_search_value(", self, ", ", arg_code, ")"); + return CORD_all("List$binary_search_value(", self, ", ", arg_code, ")"); } else if (streq(call->name, "clear")) { - EXPECT_POINTER("an", "array"); + EXPECT_POINTER("a", "list"); (void)compile_arguments(env, ast, NULL, call->args); - return CORD_all("Array$clear(", self, ")"); + return CORD_all("List$clear(", self, ")"); } else if (streq(call->name, "find")) { self = compile_to_pointer_depth(env, call->self, 0, false); arg_t *arg_spec = new(arg_t, .name="item", .type=item_t); - return CORD_all("Array$find_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), + return CORD_all("List$find_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type_info(self_value_t), ")"); } else if (streq(call->name, "first")) { self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); @@ -3182,38 +3182,38 @@ CORD compile(env_t *env, ast_t *ast) type_t *predicate_type = Type( ClosureType, .fn=NewFunctionType(Type(BoolType), {.name="item", .type=item_ptr})); arg_t *arg_spec = new(arg_t, .name="predicate", .type=predicate_type); - return CORD_all("Array$first(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); + return CORD_all("List$first(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); } else if (streq(call->name, "from")) { self = compile_to_pointer_depth(env, call->self, 0, true); arg_t *arg_spec = new(arg_t, .name="first", .type=INT_TYPE); - return CORD_all("Array$from(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); + return CORD_all("List$from(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); } else if (streq(call->name, "to")) { self = compile_to_pointer_depth(env, call->self, 0, true); arg_t *arg_spec = new(arg_t, .name="last", .type=INT_TYPE); - return CORD_all("Array$to(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); + return CORD_all("List$to(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); } else if (streq(call->name, "by")) { self = compile_to_pointer_depth(env, call->self, 0, true); arg_t *arg_spec = new(arg_t, .name="stride", .type=INT_TYPE); - return CORD_all("Array$by(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); + return CORD_all("List$by(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); } else if (streq(call->name, "reversed")) { self = compile_to_pointer_depth(env, call->self, 0, true); (void)compile_arguments(env, ast, NULL, call->args); - return CORD_all("Array$reversed(", self, ", ", padded_item_size, ")"); + return CORD_all("List$reversed(", self, ", ", padded_item_size, ")"); } else if (streq(call->name, "unique")) { self = compile_to_pointer_depth(env, call->self, 0, false); (void)compile_arguments(env, ast, NULL, call->args); return CORD_all("Table$from_entries(", self, ", Set$info(", compile_type_info(item_t), "))"); } else if (streq(call->name, "pop")) { - EXPECT_POINTER("an", "array"); + EXPECT_POINTER("a", "list"); arg_t *arg_spec = new(arg_t, .name="index", .type=INT_TYPE, .default_val=FakeAST(Int, "-1")); CORD index = compile_arguments(env, ast, arg_spec, call->args); - return CORD_all("Array$pop(", self, ", ", index, ", ", compile_type(item_t), ", _, ", + return CORD_all("List$pop(", self, ", ", index, ", ", compile_type(item_t), ", _, ", promote_to_optional(item_t, "_"), ", ", compile_none(item_t), ")"); } else if (streq(call->name, "counts")) { self = compile_to_pointer_depth(env, call->self, 0, false); (void)compile_arguments(env, ast, NULL, call->args); - return CORD_all("Array$counts(", self, ", ", compile_type_info(self_value_t), ")"); - } else code_err(ast, "There is no '", call->name, "' method for arrays"); + return CORD_all("List$counts(", self, ", ", compile_type_info(self_value_t), ")"); + } else code_err(ast, "There is no '", call->name, "' method for lists"); } case SetType: { auto set = Match(self_value_t, SetType); @@ -3229,9 +3229,9 @@ CORD compile(env_t *env, ast_t *ast) compile_type_info(self_value_t), ")"); } else if (streq(call->name, "add_all")) { EXPECT_POINTER("a", "set"); - arg_t *arg_spec = new(arg_t, .name="items", .type=Type(ArrayType, .item_type=Match(self_value_t, SetType)->item_type)); + arg_t *arg_spec = new(arg_t, .name="items", .type=Type(ListType, .item_type=Match(self_value_t, SetType)->item_type)); return CORD_all("({ Table_t *set = ", self, "; ", - "Array_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ", + "List_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ", "for (int64_t i = 0; i < to_add.length; i++)\n" "Table$set(set, to_add.data + i*to_add.stride, NULL, ", compile_type_info(self_value_t), ");\n", "(void)0; })"); @@ -3242,9 +3242,9 @@ CORD compile(env_t *env, ast_t *ast) compile_type_info(self_value_t), ")"); } else if (streq(call->name, "remove_all")) { EXPECT_POINTER("a", "set"); - arg_t *arg_spec = new(arg_t, .name="items", .type=Type(ArrayType, .item_type=Match(self_value_t, SetType)->item_type)); + arg_t *arg_spec = new(arg_t, .name="items", .type=Type(ListType, .item_type=Match(self_value_t, SetType)->item_type)); return CORD_all("({ Table_t *set = ", self, "; ", - "Array_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ", + "List_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ", "for (int64_t i = 0; i < to_add.length; i++)\n" "Table$remove(set, to_add.data + i*to_add.stride, ", compile_type_info(self_value_t), ");\n", "(void)0; })"); @@ -3417,8 +3417,8 @@ CORD compile(env_t *env, ast_t *ast) case Deserialize: { ast_t *value = Match(ast, Deserialize)->value; type_t *value_type = get_type(env, value); - if (!type_eq(value_type, Type(ArrayType, Type(ByteType)))) - code_err(value, "This value should be an array of bytes, not a ", type_to_str(value_type)); + if (!type_eq(value_type, Type(ListType, Type(ByteType)))) + code_err(value, "This value should be a list of bytes, not a ", type_to_str(value_type)); type_t *t = parse_type_ast(env, Match(ast, Deserialize)->type); return CORD_all("({ ", compile_declaration(t, "deserialized"), ";\n" "generic_deserialize(", compile(env, value), ", &deserialized, ", compile_type_info(t), ");\n" @@ -3716,14 +3716,14 @@ CORD compile(env_t *env, ast_t *ast) } code_err(ast, "The field '", f->field, "' is not a valid tag name of ", type_to_str(value_t)); } - case ArrayType: { + case ListType: { if (streq(f->field, "length")) return CORD_all("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").length)"); - code_err(ast, "There is no ", f->field, " field on arrays"); + code_err(ast, "There is no ", f->field, " field on lists"); } case SetType: { if (streq(f->field, "items")) - return CORD_all("ARRAY_COPY((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries)"); + return CORD_all("LIST_COPY((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries)"); else if (streq(f->field, "length")) return CORD_all("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries.length)"); code_err(ast, "There is no '", f->field, "' field on sets"); @@ -3732,13 +3732,13 @@ CORD compile(env_t *env, ast_t *ast) if (streq(f->field, "length")) { return CORD_all("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries.length)"); } else if (streq(f->field, "keys")) { - return CORD_all("ARRAY_COPY((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries)"); + return CORD_all("LIST_COPY((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries)"); } else if (streq(f->field, "values")) { auto table = Match(value_t, TableType); CORD offset = CORD_all("offsetof(struct { ", compile_declaration(table->key_type, "k"), "; ", compile_declaration(table->value_type, "v"), "; }, v)"); - return CORD_all("({ Array_t *entries = &(", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries;\n" - "ARRAY_INCREF(*entries);\n" - "Array_t values = *entries;\n" + return CORD_all("({ List_t *entries = &(", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries;\n" + "LIST_INCREF(*entries);\n" + "List_t values = *entries;\n" "values.data += ", offset, ";\n" "values; })"); } else if (streq(f->field, "fallback")) { @@ -3762,8 +3762,8 @@ CORD compile(env_t *env, ast_t *ast) if (indexed_type->tag != PointerType) code_err(ast, "Only pointers can use the '[]' operator to dereference the entire value."); auto ptr = Match(indexed_type, PointerType); - if (ptr->pointed->tag == ArrayType) { - return CORD_all("*({ Array_t *arr = ", compile(env, indexing->indexed), "; ARRAY_INCREF(*arr); arr; })"); + if (ptr->pointed->tag == ListType) { + return CORD_all("*({ List_t *list = ", compile(env, indexing->indexed), "; LIST_INCREF(*list); list; })"); } else if (ptr->pointed->tag == TableType || ptr->pointed->tag == SetType) { return CORD_all("*({ Table_t *t = ", compile(env, indexing->indexed), "; TABLE_INCREF(*t); t; })"); } else { @@ -3773,20 +3773,20 @@ CORD compile(env_t *env, ast_t *ast) type_t *container_t = value_type(indexed_type); type_t *index_t = get_type(env, indexing->index); - if (container_t->tag == ArrayType) { + if (container_t->tag == ListType) { if (index_t->tag != IntType && index_t->tag != BigIntType && index_t->tag != ByteType) - code_err(indexing->index, "Arrays can only be indexed by integers, not ", type_to_str(index_t)); - type_t *item_type = Match(container_t, ArrayType)->item_type; - CORD arr = compile_to_pointer_depth(env, indexing->indexed, 0, false); + code_err(indexing->index, "Lists can only be indexed by integers, not ", type_to_str(index_t)); + type_t *item_type = Match(container_t, ListType)->item_type; + CORD list = compile_to_pointer_depth(env, indexing->indexed, 0, false); file_t *f = indexing->index->file; CORD index_code = indexing->index->tag == Int ? compile_int_to_type(env, indexing->index, Type(IntType, .bits=TYPE_IBITS64)) : (index_t->tag == BigIntType ? CORD_all("Int64$from_int(", compile(env, indexing->index), ", no)") : CORD_all("(Int64_t)(", compile(env, indexing->index), ")")); if (indexing->unchecked) - return CORD_all("Array_get_unchecked(", compile_type(item_type), ", ", arr, ", ", index_code, ")"); + return CORD_all("List_get_unchecked(", compile_type(item_type), ", ", list, ", ", index_code, ")"); else - return CORD_all("Array_get(", compile_type(item_type), ", ", arr, ", ", index_code, ", ", + return CORD_all("List_get(", compile_type(item_type), ", ", list, ", ", index_code, ", ", CORD_asprintf("%ld", (int64_t)(indexing->index->start - f->text)), ", ", CORD_asprintf("%ld", (int64_t)(indexing->index->end - f->text)), ")"); @@ -3859,9 +3859,9 @@ CORD compile_type_info(type_t *t) auto e = Match(t, EnumType); return CORD_all("(&", namespace_prefix(e->env, e->env->namespace->parent), e->name, "$$info)"); } - case ArrayType: { - type_t *item_t = Match(t, ArrayType)->item_type; - return CORD_all("Array$info(", compile_type_info(item_t), ")"); + case ListType: { + type_t *item_t = Match(t, ListType)->item_type; + return CORD_all("List$info(", compile_type_info(item_t), ")"); } case SetType: { type_t *item_type = Match(t, SetType)->item_type; @@ -3950,7 +3950,7 @@ CORD compile_cli_arg_call(env_t *env, CORD fn_name, type_t *fn_type) } else { if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType)) usage = CORD_all(usage, "[--", flag, "]"); - else if (t->tag == ArrayType) + else if (t->tag == ListType) usage = CORD_all(usage, "[--", flag, " ", get_flag_options(t, "|"), "]"); else usage = CORD_all(usage, "[--", flag, "=", get_flag_options(t, "|"), "]"); @@ -3960,7 +3960,7 @@ CORD compile_cli_arg_call(env_t *env, CORD fn_name, type_t *fn_type) usage = CORD_all(usage, "<--", flag, "|--no-", flag, ">"); else if (t->tag == EnumType) usage = CORD_all(usage, get_flag_options(t, "|")); - else if (t->tag == ArrayType) + else if (t->tag == ListType) usage = CORD_all(usage, "[", flag, "...]"); else usage = CORD_all(usage, "<", flag, ">"); diff --git a/src/environment.c b/src/environment.c index 8084758e..0d55be9c 100644 --- a/src/environment.c +++ b/src/environment.c @@ -76,20 +76,20 @@ env_t *global_env(void) type_t *type; CORD typename; CORD typeinfo; - Array_t namespace; + List_t namespace; } global_types[] = { {"Void", Type(VoidType), "Void_t", "Void$info", {}}, {"Abort", Type(AbortType), "void", "Abort$info", {}}, {"Memory", Type(MemoryType), "Memory_t", "Memory$info", {}}, - {"Bool", Type(BoolType), "Bool_t", "Bool$info", TypedArray(ns_entry_t, + {"Bool", Type(BoolType), "Bool_t", "Bool$info", TypedList(ns_entry_t, {"parse", "Bool$parse", "func(text:Text -> Bool?)"}, )}, - {"Byte", Type(ByteType), "Byte_t", "Byte$info", TypedArray(ns_entry_t, + {"Byte", Type(ByteType), "Byte_t", "Byte$info", TypedList(ns_entry_t, {"max", "Byte$max", "Byte"}, {"hex", "Byte$hex", "func(byte:Byte, uppercase=yes, prefix=no -> Text)"}, {"min", "Byte$min", "Byte"}, )}, - {"Int", Type(BigIntType), "Int_t", "Int$info", TypedArray(ns_entry_t, + {"Int", Type(BigIntType), "Int_t", "Int$info", TypedList(ns_entry_t, {"abs", "Int$abs", "func(x:Int -> Int)"}, {"bit_and", "Int$bit_and", "func(x,y:Int -> Int)"}, {"bit_or", "Int$bit_or", "func(x,y:Int -> Int)"}, @@ -124,7 +124,7 @@ env_t *global_env(void) {"times", "Int$times", "func(x,y:Int -> Int)"}, {"to", "Int$to", "func(first:Int,last:Int,step:Int?=none -> func(->Int?))"}, )}, - {"Int64", Type(IntType, .bits=TYPE_IBITS64), "Int64_t", "Int64$info", TypedArray(ns_entry_t, + {"Int64", Type(IntType, .bits=TYPE_IBITS64), "Int64_t", "Int64$info", TypedList(ns_entry_t, {"abs", "labs", "func(i:Int64 -> Int64)"}, {"bits", "Int64$bits", "func(x:Int64 -> [Bool])"}, {"clamped", "Int64$clamped", "func(x,low,high:Int64 -> Int64)"}, @@ -145,7 +145,7 @@ env_t *global_env(void) {"wrapping_minus", "Int64$wrapping_minus", "func(x:Int64,y:Int64 -> Int64)"}, {"wrapping_plus", "Int64$wrapping_plus", "func(x:Int64,y:Int64 -> Int64)"}, )}, - {"Int32", Type(IntType, .bits=TYPE_IBITS32), "Int32_t", "Int32$info", TypedArray(ns_entry_t, + {"Int32", Type(IntType, .bits=TYPE_IBITS32), "Int32_t", "Int32$info", TypedList(ns_entry_t, {"abs", "abs", "func(i:Int32 -> Int32)"}, {"bits", "Int32$bits", "func(x:Int32 -> [Bool])"}, {"clamped", "Int32$clamped", "func(x,low,high:Int32 -> Int32)"}, @@ -166,7 +166,7 @@ env_t *global_env(void) {"wrapping_minus", "Int32$wrapping_minus", "func(x:Int32,y:Int32 -> Int32)"}, {"wrapping_plus", "Int32$wrapping_plus", "func(x:Int32,y:Int32 -> Int32)"}, )}, - {"Int16", Type(IntType, .bits=TYPE_IBITS16), "Int16_t", "Int16$info", TypedArray(ns_entry_t, + {"Int16", Type(IntType, .bits=TYPE_IBITS16), "Int16_t", "Int16$info", TypedList(ns_entry_t, {"abs", "abs", "func(i:Int16 -> Int16)"}, {"bits", "Int16$bits", "func(x:Int16 -> [Bool])"}, {"clamped", "Int16$clamped", "func(x,low,high:Int16 -> Int16)"}, @@ -187,7 +187,7 @@ env_t *global_env(void) {"wrapping_minus", "Int16$wrapping_minus", "func(x:Int16,y:Int16 -> Int16)"}, {"wrapping_plus", "Int16$wrapping_plus", "func(x:Int16,y:Int16 -> Int16)"}, )}, - {"Int8", Type(IntType, .bits=TYPE_IBITS8), "Int8_t", "Int8$info", TypedArray(ns_entry_t, + {"Int8", Type(IntType, .bits=TYPE_IBITS8), "Int8_t", "Int8$info", TypedList(ns_entry_t, {"abs", "abs", "func(i:Int8 -> Int8)"}, {"bits", "Int8$bits", "func(x:Int8 -> [Bool])"}, {"clamped", "Int8$clamped", "func(x,low,high:Int8 -> Int8)"}, @@ -212,7 +212,7 @@ env_t *global_env(void) #define F(name) {#name, #name, "func(n:Num -> Num)"} #define F_opt(name) {#name, #name, "func(n:Num -> Num?)"} #define F2(name) {#name, #name, "func(x,y:Num -> Num)"} - {"Num", Type(NumType, .bits=TYPE_NBITS64), "Num_t", "Num$info", TypedArray(ns_entry_t, + {"Num", Type(NumType, .bits=TYPE_NBITS64), "Num_t", "Num$info", TypedList(ns_entry_t, {"near", "Num$near", "func(x,y:Num, ratio=1e-9, min_epsilon=1e-9 -> Bool)"}, {"clamped", "Num$clamped", "func(x,low,high:Num -> Num)"}, {"format", "Num$format", "func(n:Num, precision=16 -> Text)"}, @@ -244,7 +244,7 @@ env_t *global_env(void) #define F(name) {#name, #name"f", "func(n:Num32 -> Num32)"} #define F_opt(name) {#name, #name"f", "func(n:Num32 -> Num32?)"} #define F2(name) {#name, #name"f", "func(x,y:Num32 -> Num32)"} - {"Num32", Type(NumType, .bits=TYPE_NBITS32), "Num32_t", "Num32$info", TypedArray(ns_entry_t, + {"Num32", Type(NumType, .bits=TYPE_NBITS32), "Num32_t", "Num32$info", TypedList(ns_entry_t, {"near", "Num32$near", "func(x,y:Num32, ratio=Num32(1e-9), min_epsilon=Num32(1e-9) -> Bool)"}, {"clamped", "Num32$clamped", "func(x,low,high:Num32 -> Num32)"}, {"format", "Num32$format", "func(n:Num32, precision=8 -> Text)"}, @@ -268,19 +268,19 @@ env_t *global_env(void) F_opt(tan), F(tanh), F_opt(tgamma), F(trunc), F_opt(y0), F_opt(y1), F2(atan2), F2(copysign), F2(fdim), F2(hypot), F2(nextafter), )}, - {"CString", Type(CStringType), "char*", "CString$info", TypedArray(ns_entry_t, + {"CString", Type(CStringType), "char*", "CString$info", TypedList(ns_entry_t, {"as_text", "CString$as_text_simple", "func(str:CString -> Text)"}, )}, #undef F2 #undef F_opt #undef F #undef C - {"PathType", PATH_TYPE_TYPE, "PathType_t", "PathType$info", TypedArray(ns_entry_t, + {"PathType", PATH_TYPE_TYPE, "PathType_t", "PathType$info", TypedList(ns_entry_t, {"Relative", "((PathType_t){.$tag=PATH_RELATIVE})", "PathType"}, {"Absolute", "((PathType_t){.$tag=PATH_ABSOLUTE})", "PathType"}, {"Home", "((PathType_t){.$tag=PATH_HOME})", "PathType"}, )}, - {"Path", PATH_TYPE, "Path_t", "Path$info", TypedArray(ns_entry_t, + {"Path", PATH_TYPE, "Path_t", "Path$info", TypedList(ns_entry_t, {"accessed", "Path$accessed", "func(path:Path, follow_symlinks=yes -> Int64?)"}, {"append", "Path$append", "func(path:Path, text:Text, permissions=Int32(0o644))"}, {"append_bytes", "Path$append_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644))"}, @@ -322,7 +322,7 @@ env_t *global_env(void) {"write_unique", "Path$write_unique", "func(path:Path, text:Text -> Path)"}, {"write_unique_bytes", "Path$write_unique_bytes", "func(path:Path, bytes:[Byte] -> Path)"}, )}, - {"Text", TEXT_TYPE, "Text_t", "Text$info", TypedArray(ns_entry_t, + {"Text", TEXT_TYPE, "Text_t", "Text$info", TypedList(ns_entry_t, {"as_c_string", "Text$as_c_string", "func(text:Text -> CString)"}, {"at", "Text$cluster", "func(text:Text, index:Int -> Text)"}, {"by_line", "Text$by_line", "func(text:Text -> func(->Text?))"}, @@ -406,7 +406,7 @@ env_t *global_env(void) struct { const char *c_name, *type_str; } constructor_infos[] = {__VA_ARGS__}; \ for (size_t i = 0; i < sizeof(constructor_infos)/sizeof(constructor_infos[0]); i++) { \ type_t *t = parse_type_string(ns_env, constructor_infos[i].type_str); \ - Array$insert(&ns_env->namespace->constructors, \ + List$insert(&ns_env->namespace->constructors, \ ((binding_t[1]){{.code=constructor_infos[i].c_name, \ .type=Match(t, ClosureType)->fn}}), I(0), sizeof(binding_t)); \ } \ @@ -597,8 +597,8 @@ env_t *for_scope(env_t *env, ast_t *ast) env_t *scope = fresh_scope(env); switch (iter_t->tag) { - case ArrayType: { - type_t *item_t = Match(iter_t, ArrayType)->item_type; + case ListType: { + type_t *item_t = Match(iter_t, ListType)->item_type; const char *vars[2] = {}; int64_t num_vars = 0; for (ast_list_t *var = for_->vars; var; var = var->next) { @@ -672,7 +672,7 @@ env_t *get_namespace_by_type(env_t *env, type_t *t) { t = value_type(t); switch (t->tag) { - case ArrayType: return NULL; + case ListType: return NULL; case TableType: return NULL; case CStringType: case BoolType: case IntType: case BigIntType: case NumType: case ByteType: { @@ -730,7 +730,7 @@ PUREFUNC binding_t *get_constructor(env_t *env, type_t *t, arg_ast_t *args) { env_t *type_env = get_namespace_by_type(env, t); if (!type_env) return NULL; - Array_t constructors = type_env->namespace->constructors; + List_t constructors = type_env->namespace->constructors; // Prioritize exact matches: for (int64_t i = constructors.length-1; i >= 0; i--) { binding_t *b = constructors.data + i*constructors.stride; diff --git a/src/environment.h b/src/environment.h index cbaae09b..1484a4c4 100644 --- a/src/environment.h +++ b/src/environment.h @@ -33,7 +33,7 @@ typedef struct loop_ctx_s { typedef struct namespace_s { const char *name; - Array_t constructors; + List_t constructors; struct namespace_s *parent; } namespace_t; diff --git a/src/parse.c b/src/parse.c index b9a695b9..d142e780 100644 --- a/src/parse.c +++ b/src/parse.c @@ -89,7 +89,7 @@ static ast_t *parse_non_optional_suffix(parse_ctx_t *ctx, ast_t *lhs); static ast_t *parse_optional_conditional_suffix(parse_ctx_t *ctx, ast_t *stmt); static ast_t *parse_optional_suffix(parse_ctx_t *ctx, ast_t *lhs); static arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos); -static type_ast_t *parse_array_type(parse_ctx_t *ctx, const char *pos); +static type_ast_t *parse_list_type(parse_ctx_t *ctx, const char *pos); static type_ast_t *parse_func_type(parse_ctx_t *ctx, const char *pos); static type_ast_t *parse_non_optional_type(parse_ctx_t *ctx, const char *pos); static type_ast_t *parse_pointer_type(parse_ctx_t *ctx, const char *pos); @@ -97,7 +97,7 @@ static type_ast_t *parse_set_type(parse_ctx_t *ctx, const char *pos); static type_ast_t *parse_table_type(parse_ctx_t *ctx, const char *pos); static type_ast_t *parse_type(parse_ctx_t *ctx, const char *pos); static type_ast_t *parse_type_name(parse_ctx_t *ctx, const char *pos); -static PARSER(parse_array); +static PARSER(parse_list); static PARSER(parse_assignment); static PARSER(parse_block); static PARSER(parse_bool); @@ -564,13 +564,13 @@ type_ast_t *parse_func_type(parse_ctx_t *ctx, const char *pos) { return NewTypeAST(ctx->file, start, pos, FunctionTypeAST, .args=args, .ret=ret); } -type_ast_t *parse_array_type(parse_ctx_t *ctx, const char *pos) { +type_ast_t *parse_list_type(parse_ctx_t *ctx, const char *pos) { const char *start = pos; if (!match(&pos, "[")) return NULL; type_ast_t *type = expect(ctx, start, &pos, parse_type, - "I couldn't parse an array item type after this point"); - expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this array type"); - return NewTypeAST(ctx->file, start, pos, ArrayTypeAST, .item=type); + "I couldn't parse a list item type after this point"); + expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this list type"); + return NewTypeAST(ctx->file, start, pos, ListTypeAST, .item=type); } type_ast_t *parse_pointer_type(parse_ctx_t *ctx, const char *pos) { @@ -614,7 +614,7 @@ type_ast_t *parse_non_optional_type(parse_ctx_t *ctx, const char *pos) { type_ast_t *type = NULL; bool success = (false || (type=parse_pointer_type(ctx, pos)) - || (type=parse_array_type(ctx, pos)) + || (type=parse_list_type(ctx, pos)) || (type=parse_table_type(ctx, pos)) || (type=parse_set_type(ctx, pos)) || (type=parse_type_name(ctx, pos)) @@ -698,7 +698,7 @@ static INLINE bool match_separator(const char **pos) { // Either comma or newlin } } -PARSER(parse_array) { +PARSER(parse_list) { const char *start = pos; if (!match(&pos, "[")) return NULL; @@ -719,10 +719,10 @@ PARSER(parse_array) { break; } whitespace(&pos); - expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this array"); + expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this list"); REVERSE_LIST(items); - return NewAST(ctx->file, start, pos, Array, .items=items); + return NewAST(ctx->file, start, pos, List, .items=items); } PARSER(parse_table) { @@ -1466,7 +1466,7 @@ PARSER(parse_term_no_suffix) { || (term=parse_set(ctx, pos)) || (term=parse_deserialize(ctx, pos)) || (term=parse_var(ctx, pos)) - || (term=parse_array(ctx, pos)) + || (term=parse_list(ctx, pos)) || (term=parse_reduction(ctx, pos)) || (term=parse_pass(ctx, pos)) || (term=parse_defer(ctx, pos)) @@ -128,10 +128,10 @@ const TypeInfo_t *type_to_type_info(type_t *t) default: print_err("Invalid bits"); } case TextType: return &Text$info; - case ArrayType: { - const TypeInfo_t *item_info = type_to_type_info(Match(t, ArrayType)->item_type); - TypeInfo_t array_info = *Array$info(item_info); - return memcpy(GC_MALLOC(sizeof(TypeInfo_t)), &array_info, sizeof(TypeInfo_t)); + case ListType: { + const TypeInfo_t *item_info = type_to_type_info(Match(t, ListType)->item_type); + TypeInfo_t list_info = *List$info(item_info); + return memcpy(GC_MALLOC(sizeof(TypeInfo_t)), &list_info, sizeof(TypeInfo_t)); } case TableType: { const TypeInfo_t *key_info = type_to_type_info(Match(t, TableType)->key_type); @@ -391,17 +391,17 @@ void eval(env_t *env, ast_t *ast, void *dest) type_t *indexed_t = get_type(env, index->indexed); // type_t *index_t = get_type(env, index->index); switch (indexed_t->tag) { - case ArrayType: { - Array_t arr; - eval(env, index->indexed, &arr); + case ListType: { + List_t list; + eval(env, index->indexed, &list); int64_t raw_index = Int64$from_int(ast_to_int(env, index->index), false); int64_t index_int = raw_index; - if (index_int < 1) index_int = arr.length + index_int + 1; - if (index_int < 1 || index_int > arr.length) + if (index_int < 1) index_int = list.length + index_int + 1; + if (index_int < 1 || index_int > list.length) print_err(raw_index, - " is an invalid index for an array with length ", (int64_t)arr.length); - size_t item_size = type_size(Match(indexed_t, ArrayType)->item_type); - memcpy(dest, arr.data + arr.stride*(index_int-1), item_size); + " is an invalid index for a list with length ", (int64_t)list.length); + size_t item_size = type_size(Match(indexed_t, ListType)->item_type); + memcpy(dest, list.data + list.stride*(index_int-1), item_size); break; } case TableType: { @@ -427,16 +427,16 @@ void eval(env_t *env, ast_t *ast, void *dest) } break; } - case Array: { - assert(t->tag == ArrayType); - Array_t arr = {}; - size_t item_size = type_size(Match(t, ArrayType)->item_type); + case List: { + assert(t->tag == ListType); + List_t list = {}; + size_t item_size = type_size(Match(t, ListType)->item_type); char item_buf[item_size]; - for (ast_list_t *item = Match(ast, Array)->items; item; item = item->next) { + for (ast_list_t *item = Match(ast, List)->items; item; item = item->next) { eval(env, item->ast, item_buf); - Array$insert(&arr, item_buf, I(0), (int64_t)type_size(Match(t, ArrayType)->item_type)); + List$insert(&list, item_buf, I(0), (int64_t)type_size(Match(t, ListType)->item_type)); } - memcpy(dest, &arr, sizeof(Array_t)); + memcpy(dest, &list, sizeof(List_t)); break; } case Table: { diff --git a/src/stdlib/README.md b/src/stdlib/README.md index 1c72d3d3..671d330a 100644 --- a/src/stdlib/README.md +++ b/src/stdlib/README.md @@ -16,7 +16,7 @@ some common functionality. ## Core Data Types - Datatypes (type definitions): [datatypes.h](datatypes.h), [datatypes.c](datatypes.c) -- Arrays: [arrays.h](arrays.h), [arrays.c](arrays.c) +- Lists: [lists.h](lists.h), [lists.c](lists.c) - Bools: [bools.h](bools.h), [bools.c](bools.c) - Bytes: [bytes.h](bytes.h), [bytes.c](bytes.c) - C Strings: [c_strings.h](c_strings.h), [c_strings.c](c_strings.c) diff --git a/src/stdlib/arrays.c b/src/stdlib/arrays.c deleted file mode 100644 index c8b82cf4..00000000 --- a/src/stdlib/arrays.c +++ /dev/null @@ -1,813 +0,0 @@ -// Functions that operate on arrays - -#include <gc.h> -#include <stdbool.h> -#include <stdint.h> -#include <sys/param.h> - -#include "arrays.h" -#include "integers.h" -#include "math.h" -#include "metamethods.h" -#include "optionals.h" -#include "tables.h" -#include "text.h" -#include "util.h" - -// Use inline version of siphash code: -#include "siphash.h" -#include "siphash-internals.h" - -PUREFUNC static INLINE int64_t get_padded_item_size(const TypeInfo_t *info) -{ - int64_t size = info->ArrayInfo.item->size; - if (info->ArrayInfo.item->align > 1 && size % info->ArrayInfo.item->align) - errx(1, "Item size is not padded!"); - return size; -} - -// Replace the array's .data pointer with a new pointer to a copy of the -// data that is compacted and has a stride of exactly `padded_item_size` -public void Array$compact(Array_t *arr, int64_t padded_item_size) -{ - void *copy = NULL; - if (arr->length > 0) { - copy = arr->atomic ? GC_MALLOC_ATOMIC((size_t)arr->length * (size_t)padded_item_size) - : GC_MALLOC((size_t)arr->length * (size_t)padded_item_size); - if ((int64_t)arr->stride == padded_item_size) { - memcpy(copy, arr->data, (size_t)arr->length * (size_t)padded_item_size); - } else { - for (int64_t i = 0; i < arr->length; i++) - memcpy(copy + i*padded_item_size, arr->data + arr->stride*i, (size_t)padded_item_size); - } - } - *arr = (Array_t){ - .data=copy, - .length=arr->length, - .stride=padded_item_size, - .atomic=arr->atomic, - }; -} - -public void Array$insert(Array_t *arr, const void *item, Int_t int_index, int64_t padded_item_size) -{ - int64_t index = Int64$from_int(int_index, false); - if (index <= 0) index = arr->length + index + 1; - - if (index < 1) index = 1; - else if (index > (int64_t)arr->length + 1) - fail("Invalid insertion index ", index, " for an array with length ", (int64_t)arr->length); - - if (!arr->data) { - arr->free = 4; - arr->data = arr->atomic ? GC_MALLOC_ATOMIC((size_t)arr->free * (size_t)padded_item_size) - : GC_MALLOC((size_t)arr->free * (size_t)padded_item_size); - arr->stride = padded_item_size; - } else if (arr->free < 1 || arr->data_refcount != 0 || (int64_t)arr->stride != padded_item_size) { - // Resize policy: +50% growth (clamped between 8 and ARRAY_MAX_FREE_ENTRIES) - arr->free = MIN(ARRAY_MAX_FREE_ENTRIES, MAX(8, arr->length)/2); - void *copy = arr->atomic ? GC_MALLOC_ATOMIC((size_t)(arr->length + arr->free) * (size_t)padded_item_size) - : GC_MALLOC((size_t)(arr->length + arr->free) * (size_t)padded_item_size); - for (int64_t i = 0; i < index-1; i++) - memcpy(copy + i*padded_item_size, arr->data + arr->stride*i, (size_t)padded_item_size); - for (int64_t i = index-1; i < (int64_t)arr->length; i++) - memcpy(copy + (i+1)*padded_item_size, arr->data + arr->stride*i, (size_t)padded_item_size); - arr->data = copy; - arr->data_refcount = 0; - arr->stride = padded_item_size; - } else { - if (index != arr->length+1) - memmove( - arr->data + index*padded_item_size, - arr->data + (index-1)*padded_item_size, - (size_t)((arr->length - index + 1)*padded_item_size)); - } - assert(arr->free > 0); - --arr->free; - ++arr->length; - memcpy((void*)arr->data + (index-1)*padded_item_size, item, (size_t)padded_item_size); -} - -public void Array$insert_all(Array_t *arr, Array_t to_insert, Int_t int_index, int64_t padded_item_size) -{ - int64_t index = Int64$from_int(int_index, false); - if (to_insert.length == 0) - return; - - if (!arr->data) { - *arr = to_insert; - ARRAY_INCREF(*arr); - return; - } - - if (index < 1) index = arr->length + index + 1; - - if (index < 1) index = 1; - else if (index > (int64_t)arr->length + 1) - fail("Invalid insertion index ", index, " for an array with length ", (int64_t)arr->length); - - if ((int64_t)arr->free >= (int64_t)to_insert.length // Adequate free space - && arr->data_refcount == 0 // Not aliased memory - && (int64_t)arr->stride == padded_item_size) { // Contiguous array - // If we can fit this within the array's preallocated free space, do that: - arr->free -= to_insert.length; - arr->length += to_insert.length; - if (index != arr->length+1) - memmove((void*)arr->data + index*padded_item_size, - arr->data + (index-1)*padded_item_size, - (size_t)((arr->length - index + to_insert.length-1)*padded_item_size)); - for (int64_t i = 0; i < to_insert.length; i++) - memcpy((void*)arr->data + (index-1 + i)*padded_item_size, - to_insert.data + i*to_insert.stride, (size_t)padded_item_size); - } else { - // Otherwise, allocate a new chunk of memory for the array and populate it: - int64_t new_len = arr->length + to_insert.length; - arr->free = MIN(ARRAY_MAX_FREE_ENTRIES, MAX(8, new_len/4)); - void *data = arr->atomic ? GC_MALLOC_ATOMIC((size_t)((new_len + arr->free) * padded_item_size)) - : GC_MALLOC((size_t)((new_len + arr->free) * padded_item_size)); - void *p = data; - - // Copy first chunk of `arr` if needed: - if (index > 1) { - if (arr->stride == padded_item_size) { - memcpy(p, arr->data, (size_t)((index-1)*padded_item_size)); - p += (index-1)*padded_item_size; - } else { - for (int64_t i = 0; i < index-1; i++) { - memcpy(p, arr->data + arr->stride*i, (size_t)padded_item_size); - p += padded_item_size; - } - } - } - - // Copy `to_insert` - if (to_insert.stride == padded_item_size) { - memcpy(p, to_insert.data, (size_t)(to_insert.length*padded_item_size)); - p += to_insert.length*padded_item_size; - } else { - for (int64_t i = 0; i < index-1; i++) { - memcpy(p, to_insert.data + to_insert.stride*i, (size_t)padded_item_size); - p += padded_item_size; - } - } - - // Copy last chunk of `arr` if needed: - if (index < arr->length + 1) { - if (arr->stride == padded_item_size) { - memcpy(p, arr->data + padded_item_size*(index-1), (size_t)((arr->length - index + 1)*padded_item_size)); - p += (arr->length - index + 1)*padded_item_size; - } else { - for (int64_t i = index-1; i < arr->length-1; i++) { - memcpy(p, arr->data + arr->stride*i, (size_t)padded_item_size); - p += padded_item_size; - } - } - } - arr->length = new_len; - arr->stride = padded_item_size; - arr->data = data; - arr->data_refcount = 0; - } -} - -public void Array$remove_at(Array_t *arr, Int_t int_index, Int_t int_count, int64_t padded_item_size) -{ - int64_t index = Int64$from_int(int_index, false); - if (index < 1) index = arr->length + index + 1; - - int64_t count = Int64$from_int(int_count, false); - if (index < 1 || index > (int64_t)arr->length || count < 1) return; - - if (count > arr->length - index + 1) - count = (arr->length - index) + 1; - - if (index == 1) { - arr->data += arr->stride * count; - } else if (index + count > arr->length) { - arr->free += count; - } else if (arr->data_refcount != 0 || (int64_t)arr->stride != padded_item_size) { - void *copy = arr->atomic ? GC_MALLOC_ATOMIC((size_t)((arr->length-1) * padded_item_size)) - : GC_MALLOC((size_t)((arr->length-1) * padded_item_size)); - for (int64_t src = 1, dest = 1; src <= (int64_t)arr->length; src++) { - if (src < index || src >= index + count) { - memcpy(copy + (dest - 1)*padded_item_size, arr->data + arr->stride*(src - 1), (size_t)padded_item_size); - ++dest; - } - } - arr->data = copy; - arr->free = 0; - arr->data_refcount = 0; - } else { - memmove((void*)arr->data + (index-1)*padded_item_size, arr->data + (index-1 + count)*padded_item_size, - (size_t)((arr->length - index + count - 1)*padded_item_size)); - arr->free += count; - } - arr->length -= count; - if (arr->length == 0) arr->data = NULL; -} - -public void Array$remove_item(Array_t *arr, void *item, Int_t max_removals, const TypeInfo_t *type) -{ - int64_t padded_item_size = get_padded_item_size(type); - const Int_t ZERO = (Int_t){.small=(0<<2)|1}; - const Int_t ONE = (Int_t){.small=(1<<2)|1}; - const TypeInfo_t *item_type = type->ArrayInfo.item; - for (int64_t i = 0; i < arr->length; ) { - if (max_removals.small == ZERO.small) // zero - break; - - if (generic_equal(item, arr->data + i*arr->stride, item_type)) { - Array$remove_at(arr, I(i+1), ONE, padded_item_size); - max_removals = Int$minus(max_removals, ONE); - } else { - i++; - } - } -} - -public OptionalInt_t Array$find(Array_t arr, void *item, const TypeInfo_t *type) -{ - const TypeInfo_t *item_type = type->ArrayInfo.item; - for (int64_t i = 0; i < arr.length; i++) { - if (generic_equal(item, arr.data + i*arr.stride, item_type)) - return I(i+1); - } - return NONE_INT; -} - -public OptionalInt_t Array$first(Array_t arr, Closure_t predicate) -{ - bool (*is_good)(void*, void*) = (void*)predicate.fn; - for (int64_t i = 0; i < arr.length; i++) { - if (is_good(arr.data + i*arr.stride, predicate.userdata)) - return I(i+1); - } - return NONE_INT; -} - -static Closure_t _sort_comparison = {.fn=NULL}; - -int _compare_closure(const void *a, const void *b) -{ - typedef int (*comparison_t)(const void*, const void*, void*); - return ((comparison_t)_sort_comparison.fn)(a, b, _sort_comparison.userdata); -} - -public void Array$sort(Array_t *arr, Closure_t comparison, int64_t padded_item_size) -{ - if (arr->data_refcount != 0 || (int64_t)arr->stride != padded_item_size) - Array$compact(arr, padded_item_size); - - _sort_comparison = comparison; - qsort(arr->data, (size_t)arr->length, (size_t)padded_item_size, _compare_closure); -} - -public Array_t Array$sorted(Array_t arr, Closure_t comparison, int64_t padded_item_size) -{ - Array$compact(&arr, padded_item_size); - _sort_comparison = comparison; - qsort(arr.data, (size_t)arr.length, (size_t)padded_item_size, _compare_closure); - return arr; -} - -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) -static ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) { - (void)flags; - arc4random_buf(buf, buflen); - return buflen; -} -#elif defined(__linux__) -// Use getrandom() -# include <sys/random.h> -#else - #error "Unsupported platform for secure random number generation" -#endif - -static int64_t _default_random_int64(int64_t min, int64_t max, void *userdata) -{ - (void)userdata; - if (min > max) fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")"); - if (min == max) return min; - uint64_t range = (uint64_t)max - (uint64_t)min + 1; - uint64_t min_r = -range % range; - uint64_t r; - for (;;) { - getrandom(&r, sizeof(r), 0); - if (r >= min_r) break; - } - return (int64_t)((uint64_t)min + (r % range)); -} - -public void Array$shuffle(Array_t *arr, OptionalClosure_t random_int64, int64_t padded_item_size) -{ - if (arr->data_refcount != 0 || (int64_t)arr->stride != padded_item_size) - Array$compact(arr, padded_item_size); - - typedef int64_t (*rng_fn_t)(int64_t, int64_t, void*); - rng_fn_t rng_fn = random_int64.fn ? (rng_fn_t)random_int64.fn : _default_random_int64; - char tmp[padded_item_size]; - for (int64_t i = arr->length-1; i > 1; i--) { - int64_t j = rng_fn(0, i, random_int64.userdata); - if unlikely (j < 0 || j > arr->length-1) - fail("The provided random number function returned an invalid value: ", j, " (not between 0 and ", i, ")"); - memcpy(tmp, arr->data + i*padded_item_size, (size_t)padded_item_size); - memcpy((void*)arr->data + i*padded_item_size, arr->data + j*padded_item_size, (size_t)padded_item_size); - memcpy((void*)arr->data + j*padded_item_size, tmp, (size_t)padded_item_size); - } -} - -public Array_t Array$shuffled(Array_t arr, Closure_t random_int64, int64_t padded_item_size) -{ - Array$compact(&arr, padded_item_size); - Array$shuffle(&arr, random_int64, padded_item_size); - return arr; -} - -public void *Array$random(Array_t arr, OptionalClosure_t random_int64) -{ - if (arr.length == 0) - return NULL; // fail("Cannot get a random item from an empty array!"); - - typedef int64_t (*rng_fn_t)(int64_t, int64_t, void*); - rng_fn_t rng_fn = random_int64.fn ? (rng_fn_t)random_int64.fn : _default_random_int64; - int64_t index = rng_fn(0, arr.length-1, random_int64.userdata); - if unlikely (index < 0 || index > arr.length-1) - fail("The provided random number function returned an invalid value: ", index, " (not between 0 and ", (int64_t)arr.length, ")"); - return arr.data + arr.stride*index; -} - -public Table_t Array$counts(Array_t arr, const TypeInfo_t *type) -{ - Table_t counts = {}; - const TypeInfo_t count_type = *Table$info(type->ArrayInfo.item, &Int$info); - for (int64_t i = 0; i < arr.length; i++) { - void *key = arr.data + i*arr.stride; - int64_t *count = Table$get(counts, key, &count_type); - int64_t val = count ? *count + 1 : 1; - Table$set(&counts, key, &val, &count_type); - } - return counts; -} - -static double _default_random_num(void *userdata) -{ - (void)userdata; - union { - Num_t num; - uint64_t bits; - } r = {.bits=0}, one = {.num=1.0}; - getrandom((uint8_t*)&r, sizeof(r), 0); - - // Set r.num to 1.<random-bits> - r.bits &= ~(0xFFFULL << 52); - r.bits |= (one.bits & (0xFFFULL << 52)); - return r.num - 1.0; -} - -public Array_t Array$sample(Array_t arr, Int_t int_n, Array_t weights, OptionalClosure_t random_num, int64_t padded_item_size) -{ - int64_t n = Int64$from_int(int_n, false); - if (n < 0) - fail("Cannot select a negative number of values"); - - if (n == 0) - return (Array_t){}; - - if (arr.length == 0) - fail("There are no elements in this array!"); - - if (weights.length != arr.length) - fail("Array has ", (int64_t)arr.length, " elements, but there are ", (int64_t)weights.length, " weights given"); - - double total = 0.0; - for (int64_t i = 0; i < weights.length && i < arr.length; i++) { - double weight = *(double*)(weights.data + weights.stride*i); - if (isinf(weight)) - fail("Infinite weight!"); - else if (isnan(weight)) - fail("NaN weight!"); - else if (weight < 0.0) - fail("Negative weight!"); - else - total += weight; - } - - if (isinf(total)) - fail("Sample weights have overflowed to infinity"); - - if (total == 0.0) - fail("None of the given weights are nonzero"); - - double inverse_average = (double)arr.length / total; - - struct { - int64_t alias; - double odds; - } aliases[arr.length]; - - for (int64_t i = 0; i < arr.length; i++) { - double weight = i >= weights.length ? 0.0 : *(double*)(weights.data + weights.stride*i); - aliases[i].odds = weight * inverse_average; - aliases[i].alias = -1; - } - - int64_t small = 0; - for (int64_t big = 0; big < arr.length; big++) { - while (aliases[big].odds >= 1.0) { - while (small < arr.length && (aliases[small].odds >= 1.0 || aliases[small].alias != -1)) - ++small; - - if (small >= arr.length) { - aliases[big].odds = 1.0; - aliases[big].alias = big; - break; - } - - aliases[small].alias = big; - aliases[big].odds = (aliases[small].odds + aliases[big].odds) - 1.0; - } - if (big < small) small = big; - } - - for (int64_t i = small; i < arr.length; i++) - if (aliases[i].alias == -1) - aliases[i].alias = i; - - typedef double (*rng_fn_t)(void*); - rng_fn_t rng_fn = random_num.fn ? (rng_fn_t)random_num.fn : _default_random_num; - - Array_t selected = { - .data=arr.atomic ? GC_MALLOC_ATOMIC((size_t)(n * padded_item_size)) : GC_MALLOC((size_t)(n * padded_item_size)), - .length=n, - .stride=padded_item_size, .atomic=arr.atomic}; - for (int64_t i = 0; i < n; i++) { - double r = rng_fn(random_num.userdata); - if unlikely (r < 0.0 || r >= 1.0) - fail("The random number function returned a value not between 0.0 (inclusive) and 1.0 (exclusive): ", r); - r *= (double)arr.length; - int64_t index = (int64_t)r; - assert(index >= 0 && index < arr.length); - if ((r - (double)index) > aliases[index].odds) - index = aliases[index].alias; - memcpy(selected.data + i*selected.stride, arr.data + index*arr.stride, (size_t)padded_item_size); - } - return selected; -} - -public Array_t Array$from(Array_t array, Int_t first) -{ - return Array$slice(array, first, I_small(-1)); -} - -public Array_t Array$to(Array_t array, Int_t last) -{ - return Array$slice(array, I_small(1), last); -} - -public Array_t Array$by(Array_t array, Int_t int_stride, int64_t padded_item_size) -{ - int64_t stride = Int64$from_int(int_stride, false); - // In the unlikely event that the stride value would be too large to fit in - // a 15-bit integer, fall back to creating a copy of the array: - if (unlikely(array.stride*stride < ARRAY_MIN_STRIDE || array.stride*stride > ARRAY_MAX_STRIDE)) { - void *copy = NULL; - int64_t len = (stride < 0 ? array.length / -stride : array.length / stride) + ((array.length % stride) != 0); - if (len > 0) { - copy = array.atomic ? GC_MALLOC_ATOMIC((size_t)(len * padded_item_size)) : GC_MALLOC((size_t)(len * padded_item_size)); - void *start = (stride < 0 ? array.data + (array.stride * (array.length - 1)) : array.data); - for (int64_t i = 0; i < len; i++) - memcpy(copy + i*padded_item_size, start + array.stride*stride*i, (size_t)padded_item_size); - } - return (Array_t){ - .data=copy, - .length=len, - .stride=padded_item_size, - .atomic=array.atomic, - }; - } - - if (stride == 0) - return (Array_t){.atomic=array.atomic}; - - return (Array_t){ - .atomic=array.atomic, - .data=(stride < 0 ? array.data + (array.stride * (array.length - 1)) : array.data), - .length=(stride < 0 ? array.length / -stride : array.length / stride) + ((array.length % stride) != 0), - .stride=array.stride * stride, - .data_refcount=array.data_refcount, - }; -} - -public Array_t Array$slice(Array_t array, Int_t int_first, Int_t int_last) - -{ - int64_t first = Int64$from_int(int_first, false); - if (first < 0) - first = array.length + first + 1; - - int64_t last = Int64$from_int(int_last, false); - if (last < 0) - last = array.length + last + 1; - - if (last > array.length) - last = array.length; - - if (first < 1 || first > array.length || last == 0) - return (Array_t){.atomic=array.atomic}; - - return (Array_t){ - .atomic=array.atomic, - .data=array.data + array.stride*(first-1), - .length=last - first + 1, - .stride=array.stride, - .data_refcount=array.data_refcount, - }; -} - -public Array_t Array$reversed(Array_t array, int64_t padded_item_size) -{ - // Just in case negating the stride gives a value that doesn't fit into a - // 15-bit integer, fall back to Array$by()'s more general method of copying - // the array. This should only happen if array.stride is MIN_STRIDE to - // begin with (very unlikely). - if (unlikely(-array.stride < ARRAY_MIN_STRIDE || -array.stride > ARRAY_MAX_STRIDE)) - return Array$by(array, I(-1), padded_item_size); - - Array_t reversed = array; - reversed.stride = -array.stride; - reversed.data = array.data + (array.length-1)*array.stride; - return reversed; -} - -public Array_t Array$concat(Array_t x, Array_t y, int64_t padded_item_size) -{ - void *data = x.atomic ? GC_MALLOC_ATOMIC((size_t)(padded_item_size*(x.length + y.length))) - : GC_MALLOC((size_t)(padded_item_size*(x.length + y.length))); - if (x.stride == padded_item_size) { - memcpy(data, x.data, (size_t)(padded_item_size*x.length)); - } else { - for (int64_t i = 0; i < x.length; i++) - memcpy(data + i*padded_item_size, x.data + i*padded_item_size, (size_t)padded_item_size); - } - - void *dest = data + padded_item_size*x.length; - if (y.stride == padded_item_size) { - memcpy(dest, y.data, (size_t)(padded_item_size*y.length)); - } else { - for (int64_t i = 0; i < y.length; i++) - memcpy(dest + i*padded_item_size, y.data + i*y.stride, (size_t)padded_item_size); - } - - return (Array_t){ - .data=data, - .length=x.length + y.length, - .stride=padded_item_size, - .atomic=x.atomic, - }; -} - -public bool Array$has(Array_t array, void *item, const TypeInfo_t *type) -{ - const TypeInfo_t *item_type = type->ArrayInfo.item; - for (int64_t i = 0; i < array.length; i++) { - if (generic_equal(array.data + i*array.stride, item, item_type)) - return true; - } - return false; -} - -public void Array$clear(Array_t *array) -{ - *array = (Array_t){.data=0, .length=0}; -} - -public int32_t Array$compare(const void *vx, const void *vy, const TypeInfo_t *type) -{ - const Array_t *x = (Array_t*)vx, *y = (Array_t*)vy; - // Early out for arrays with the same data, e.g. two copies of the same array: - if (x->data == y->data && x->stride == y->stride) - return (x->length > y->length) - (x->length < y->length); - - const TypeInfo_t *item = type->ArrayInfo.item; - if (item->tag == PointerInfo || !item->metamethods.compare) { // data comparison - int64_t item_padded_size = type->ArrayInfo.item->size; - if (type->ArrayInfo.item->align > 1 && item_padded_size % type->ArrayInfo.item->align) - errx(1, "Item size is not padded!"); - - if ((int64_t)x->stride == item_padded_size && (int64_t)y->stride == item_padded_size && item->size == item_padded_size) { - int32_t cmp = (int32_t)memcmp(x->data, y->data, (size_t)(MIN(x->length, y->length)*item_padded_size)); - if (cmp != 0) return cmp; - } else { - for (int32_t i = 0, len = MIN(x->length, y->length); i < len; i++) { - int32_t cmp = (int32_t)memcmp(x->data+ x->stride*i, y->data + y->stride*i, (size_t)(item->size)); - if (cmp != 0) return cmp; - } - } - } else { - for (int32_t i = 0, len = MIN(x->length, y->length); i < len; i++) { - int32_t cmp = generic_compare(x->data + x->stride*i, y->data + y->stride*i, item); - if (cmp != 0) return cmp; - } - } - return (x->length > y->length) - (x->length < y->length); -} - -public bool Array$equal(const void *x, const void *y, const TypeInfo_t *type) -{ - return x == y || (((Array_t*)x)->length == ((Array_t*)y)->length && Array$compare(x, y, type) == 0); -} - -public Text_t Array$as_text(const void *obj, bool colorize, const TypeInfo_t *type) -{ - Array_t *arr = (Array_t*)obj; - if (!arr) - return Text$concat(Text("["), generic_as_text(NULL, false, type->ArrayInfo.item), Text("]")); - - const TypeInfo_t *item_type = type->ArrayInfo.item; - Text_t text = Text("["); - for (int64_t i = 0; i < arr->length; i++) { - if (i > 0) - text = Text$concat(text, Text(", ")); - Text_t item_text = generic_as_text(arr->data + i*arr->stride, colorize, item_type); - text = Text$concat(text, item_text); - } - text = Text$concat(text, Text("]")); - return text; -} - -public uint64_t Array$hash(const void *obj, const TypeInfo_t *type) -{ - const Array_t *arr = (Array_t*)obj; - const TypeInfo_t *item = type->ArrayInfo.item; - siphash sh; - siphashinit(&sh, sizeof(uint64_t[arr->length])); - if (item->tag == PointerInfo || (!item->metamethods.hash && item->size == sizeof(void*))) { // Raw data hash - for (int64_t i = 0; i < arr->length; i++) - siphashadd64bits(&sh, (uint64_t)(arr->data + i*arr->stride)); - } else { - for (int64_t i = 0; i < arr->length; i++) { - uint64_t item_hash = generic_hash(arr->data + i*arr->stride, item); - siphashadd64bits(&sh, item_hash); - } - } - return siphashfinish_last_part(&sh, 0); -} - -static void siftdown(Array_t *heap, int64_t startpos, int64_t pos, Closure_t comparison, int64_t padded_item_size) -{ - assert(pos > 0 && pos < heap->length); - char newitem[padded_item_size]; - memcpy(newitem, heap->data + heap->stride*pos, (size_t)(padded_item_size)); - while (pos > startpos) { - int64_t parentpos = (pos - 1) >> 1; - typedef int32_t (*cmp_fn_t)(void*, void*, void*); - int32_t cmp = ((cmp_fn_t)comparison.fn)(newitem, heap->data + heap->stride*parentpos, comparison.userdata); - if (cmp >= 0) - break; - - memcpy(heap->data + heap->stride*pos, heap->data + heap->stride*parentpos, (size_t)(padded_item_size)); - pos = parentpos; - } - memcpy(heap->data + heap->stride*pos, newitem, (size_t)(padded_item_size)); -} - -static void siftup(Array_t *heap, int64_t pos, Closure_t comparison, int64_t padded_item_size) -{ - int64_t endpos = heap->length; - int64_t startpos = pos; - assert(pos < endpos); - - char old_top[padded_item_size]; - memcpy(old_top, heap->data + heap->stride*pos, (size_t)(padded_item_size)); - // Bubble up the smallest leaf node - int64_t limit = endpos >> 1; - while (pos < limit) { - int64_t childpos = 2*pos + 1; // Smaller of the two child nodes - if (childpos + 1 < endpos) { - typedef int32_t (*cmp_fn_t)(void*, void*, void*); - int32_t cmp = ((cmp_fn_t)comparison.fn)( - heap->data + heap->stride*childpos, - heap->data + heap->stride*(childpos + 1), - comparison.userdata); - childpos += (cmp >= 0); - } - - // Move the child node up: - memcpy(heap->data + heap->stride*pos, heap->data + heap->stride*childpos, (size_t)(padded_item_size)); - pos = childpos; - } - memcpy(heap->data + heap->stride*pos, old_top, (size_t)(padded_item_size)); - // Shift the node's parents down: - siftdown(heap, startpos, pos, comparison, padded_item_size); -} - -public void Array$heap_push(Array_t *heap, const void *item, Closure_t comparison, int64_t padded_item_size) -{ - Array$insert(heap, item, I(0), padded_item_size); - - if (heap->length > 1) { - if (heap->data_refcount != 0) - Array$compact(heap, padded_item_size); - siftdown(heap, 0, heap->length-1, comparison, padded_item_size); - } -} - -public void Array$heap_pop(Array_t *heap, Closure_t comparison, int64_t padded_item_size) -{ - if (heap->length == 0) - fail("Attempt to pop from an empty array"); - - if (heap->length == 1) { - *heap = (Array_t){}; - } else if (heap->length == 2) { - heap->data += heap->stride; - --heap->length; - } else { - if (heap->data_refcount != 0) - Array$compact(heap, padded_item_size); - memcpy(heap->data, heap->data + heap->stride*(heap->length-1), (size_t)(padded_item_size)); - --heap->length; - siftup(heap, 0, comparison, padded_item_size); - } -} - -public void Array$heapify(Array_t *heap, Closure_t comparison, int64_t padded_item_size) -{ - if (heap->data_refcount != 0) - Array$compact(heap, padded_item_size); - - // It's necessary to bump the refcount because the user's comparison - // function could do stuff that modifies the heap's data. - ARRAY_INCREF(*heap); - int64_t i, n = heap->length; - for (i = (n >> 1) - 1 ; i >= 0 ; i--) - siftup(heap, i, comparison, padded_item_size); - ARRAY_DECREF(*heap); -} - -public Int_t Array$binary_search(Array_t array, void *target, Closure_t comparison) -{ - typedef int32_t (*cmp_fn_t)(void*, void*, void*); - int64_t lo = 0, hi = array.length-1; - while (lo <= hi) { - int64_t mid = (lo + hi) / 2; - int32_t cmp = ((cmp_fn_t)comparison.fn)( - array.data + array.stride*mid, target, comparison.userdata); - if (cmp == 0) - return I(mid+1); - else if (cmp < 0) - lo = mid + 1; - else if (cmp > 0) - hi = mid - 1; - } - return I(lo+1); // Return the index where the target would be inserted -} - -public PUREFUNC bool Array$is_none(const void *obj, const TypeInfo_t*) -{ - return ((Array_t*)obj)->length < 0; -} - -public void Array$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) -{ - Array_t arr = *(Array_t*)obj; - int64_t len = arr.length; - Int64$serialize(&len, out, pointers, &Int64$info); - auto item_serialize = type->ArrayInfo.item->metamethods.serialize; - if (item_serialize) { - for (int64_t i = 0; i < len; i++) - item_serialize(arr.data + i*arr.stride, out, pointers, type->ArrayInfo.item); - } else if (arr.stride == type->ArrayInfo.item->size) { - fwrite(arr.data, (size_t)type->ArrayInfo.item->size, (size_t)len, out); - } else { - for (int64_t i = 0; i < len; i++) - fwrite(arr.data + i*arr.stride, (size_t)type->ArrayInfo.item->size, 1, out); - } -} - -public void Array$deserialize(FILE *in, void *obj, Array_t *pointers, const TypeInfo_t *type) -{ - int64_t len = -1; - Int64$deserialize(in, &len, pointers, &Int64$info); - int64_t padded_size = type->ArrayInfo.item->size; - if (type->ArrayInfo.item->align > 0 && padded_size % type->ArrayInfo.item->align > 0) - padded_size += type->ArrayInfo.item->align - (padded_size % type->ArrayInfo.item->align); - Array_t arr = { - .length=len, - .data=GC_MALLOC((size_t)(len*padded_size)), - .stride=padded_size, - }; - auto item_deserialize = type->ArrayInfo.item->metamethods.deserialize; - if (item_deserialize) { - for (int64_t i = 0; i < len; i++) - item_deserialize(in, arr.data + i*arr.stride, pointers, type->ArrayInfo.item); - } else if (arr.stride == type->ArrayInfo.item->size) { - fread(arr.data, (size_t)type->ArrayInfo.item->size, (size_t)len, in); - } else { - for (int64_t i = 0; i < len; i++) - fread(arr.data + i*arr.stride, (size_t)type->ArrayInfo.item->size, 1, in); - } - *(Array_t*)obj = arr; -} - -// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/arrays.h b/src/stdlib/arrays.h deleted file mode 100644 index 9f5f3d00..00000000 --- a/src/stdlib/arrays.h +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once - -// Functions that operate on arrays - -#include <stdbool.h> - -#include "datatypes.h" -#include "integers.h" -#include "types.h" -#include "util.h" - -// Convert negative indices to back-indexed without branching: index0 = index + (index < 0)*(len+1)) - 1 -#define Array_get(item_type, arr_expr, index_expr, start, end) *({ \ - const Array_t arr = arr_expr; int64_t index = index_expr; \ - int64_t off = index + (index < 0) * (arr.length + 1) - 1; \ - if (unlikely(off < 0 || off >= arr.length)) \ - fail_source(__SOURCE_FILE__, start, end, "Invalid array index: ", index, " (array has length ", (int64_t)arr.length, ")\n"); \ - (item_type*)(arr.data + arr.stride * off);}) -#define Array_get_unchecked(type, x, i) *({ const Array_t arr = x; int64_t index = i; \ - int64_t off = index + (index < 0) * (arr.length + 1) - 1; \ - (type*)(arr.data + arr.stride * off);}) -#define Array_lvalue(item_type, arr_expr, index_expr, start, end) *({ \ - Array_t *arr = arr_expr; int64_t index = index_expr; \ - int64_t off = index + (index < 0) * (arr->length + 1) - 1; \ - if (unlikely(off < 0 || off >= arr->length)) \ - fail_source(__SOURCE_FILE__, start, end, "Invalid array index: ", index, " (array has length ", (int64_t)arr->length, ")\n"); \ - if (arr->data_refcount > 0) \ - Array$compact(arr, sizeof(item_type)); \ - (item_type*)(arr->data + arr->stride * off); }) -#define Array_lvalue_unchecked(item_type, arr_expr, index_expr) *({ \ - Array_t *arr = arr_expr; int64_t index = index_expr; \ - int64_t off = index + (index < 0) * (arr->length + 1) - 1; \ - if (arr->data_refcount > 0) \ - Array$compact(arr, sizeof(item_type)); \ - (item_type*)(arr->data + arr->stride * off); }) -#define Array_set(item_type, arr, index, value, start, end) \ - Array_lvalue(item_type, arr_expr, index, start, end) = value -#define is_atomic(x) _Generic(x, bool: true, int8_t: true, int16_t: true, int32_t: true, int64_t: true, float: true, double: true, default: false) -#define TypedArray(t, ...) ({ t items[] = {__VA_ARGS__}; \ - (Array_t){.length=sizeof(items)/sizeof(items[0]), \ - .stride=(int64_t)&items[1] - (int64_t)&items[0], \ - .data=memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ - .atomic=0, \ - .data_refcount=0}; }) -#define TypedArrayN(t, N, ...) ({ t items[N] = {__VA_ARGS__}; \ - (Array_t){.length=N, \ - .stride=(int64_t)&items[1] - (int64_t)&items[0], \ - .data=memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ - .atomic=0, \ - .data_refcount=0}; }) -#define Array(x, ...) ({ __typeof(x) items[] = {x, __VA_ARGS__}; \ - (Array_t){.length=sizeof(items)/sizeof(items[0]), \ - .stride=(int64_t)&items[1] - (int64_t)&items[0], \ - .data=memcpy(is_atomic(x) ? GC_MALLOC_ATOMIC(sizeof(items)) : GC_MALLOC(sizeof(items)), items, sizeof(items)), \ - .atomic=is_atomic(x), \ - .data_refcount=0}; }) -// Array refcounts use a saturating add, where once it's at the max value, it stays there. -#define ARRAY_INCREF(arr) (arr).data_refcount += ((arr).data_refcount < ARRAY_MAX_DATA_REFCOUNT) -#define ARRAY_DECREF(arr) (arr).data_refcount -= ((arr).data_refcount < ARRAY_MAX_DATA_REFCOUNT) -#define ARRAY_COPY(arr) ({ ARRAY_INCREF(arr); arr; }) - -#define Array$insert_value(arr, item_expr, index, padded_item_size) Array$insert(arr, (__typeof(item_expr)[1]){item_expr}, index, padded_item_size) -void Array$insert(Array_t *arr, const void *item, Int_t index, int64_t padded_item_size); -void Array$insert_all(Array_t *arr, Array_t to_insert, Int_t index, int64_t padded_item_size); -void Array$remove_at(Array_t *arr, Int_t index, Int_t count, int64_t padded_item_size); -void Array$remove_item(Array_t *arr, void *item, Int_t max_removals, const TypeInfo_t *type); -#define Array$remove_item_value(arr, item_expr, max, type) Array$remove_item(arr, (__typeof(item_expr)[1]){item_expr}, max, type) - -#define Array$pop(arr_expr, index_expr, item_type, nonnone_var, nonnone_expr, none_expr) ({ \ - Array_t *arr = arr_expr; \ - Int_t index = index_expr; \ - int64_t index64 = Int64$from_int(index, false); \ - int64_t off = index64 + (index64 < 0) * (arr->length + 1) - 1; \ - (off >= 0 && off < arr->length) ? ({ \ - item_type nonnone_var = *(item_type*)(arr->data + off*arr->stride); \ - Array$remove_at(arr, index, I_small(1), sizeof(item_type)); \ - nonnone_expr; \ - }) : none_expr; }) - -OptionalInt_t Array$find(Array_t arr, void *item, const TypeInfo_t *type); -#define Array$find_value(arr, item_expr, type) ({ __typeof(item_expr) item = item_expr; Array$find(arr, &item, type); }) -OptionalInt_t Array$first(Array_t arr, Closure_t predicate); -void Array$sort(Array_t *arr, Closure_t comparison, int64_t padded_item_size); -Array_t Array$sorted(Array_t arr, Closure_t comparison, int64_t padded_item_size); -void Array$shuffle(Array_t *arr, OptionalClosure_t random_int64, int64_t padded_item_size); -Array_t Array$shuffled(Array_t arr, OptionalClosure_t random_int64, int64_t padded_item_size); -void *Array$random(Array_t arr, OptionalClosure_t random_int64); -#define Array$random_value(arr, random_int64, t) ({ Array_t _arr = arr; if (_arr.length == 0) fail("Cannot get a random value from an empty array!"); *(t*)Array$random(_arr, random_int64); }) -Array_t Array$sample(Array_t arr, Int_t n, Array_t weights, Closure_t random_num, int64_t padded_item_size); -Table_t Array$counts(Array_t arr, const TypeInfo_t *type); -void Array$clear(Array_t *array); -void Array$compact(Array_t *arr, int64_t padded_item_size); -PUREFUNC bool Array$has(Array_t array, void *item, const TypeInfo_t *type); -#define Array$has_value(arr, item_expr, type) ({ __typeof(item_expr) item = item_expr; Array$has(arr, &item, type); }) -PUREFUNC Array_t Array$from(Array_t array, Int_t first); -PUREFUNC Array_t Array$to(Array_t array, Int_t last); -PUREFUNC Array_t Array$by(Array_t array, Int_t stride, int64_t padded_item_size); -PUREFUNC Array_t Array$slice(Array_t array, Int_t int_first, Int_t int_last); -PUREFUNC Array_t Array$reversed(Array_t array, int64_t padded_item_size); -Array_t Array$concat(Array_t x, Array_t y, int64_t padded_item_size); -PUREFUNC uint64_t Array$hash(const void *arr, const TypeInfo_t *type); -PUREFUNC int32_t Array$compare(const void *x, const void *y, const TypeInfo_t *type); -PUREFUNC bool Array$equal(const void *x, const void *y, const TypeInfo_t *type); -PUREFUNC bool Array$is_none(const void *obj, const TypeInfo_t*); -Text_t Array$as_text(const void *arr, bool colorize, const TypeInfo_t *type); -void Array$heapify(Array_t *heap, Closure_t comparison, int64_t padded_item_size); -void Array$heap_push(Array_t *heap, const void *item, Closure_t comparison, int64_t padded_item_size); -#define Array$heap_push_value(heap, _value, comparison, padded_item_size) ({ __typeof(_value) value = _value; Array$heap_push(heap, &value, comparison, padded_item_size); }) -void Array$heap_pop(Array_t *heap, Closure_t comparison, int64_t padded_item_size); -#define Array$heap_pop_value(heap, comparison, type, nonnone_var, nonnone_expr, none_expr) \ - ({ Array_t *_heap = heap; \ - (_heap->length > 0) ? ({ \ - type nonnone_var = *(type*)_heap->data; \ - Array$heap_pop(_heap, comparison, sizeof(type)); \ - nonnone_expr; \ - }) : none_expr; }) -Int_t Array$binary_search(Array_t array, void *target, Closure_t comparison); -#define Array$binary_search_value(array, target, comparison) \ - ({ __typeof(target) _target = target; Array$binary_search(array, &_target, comparison); }) -void Array$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type); -void Array$deserialize(FILE *in, void *obj, Array_t *pointers, const TypeInfo_t *type); - -#define Array$metamethods { \ - .as_text=Array$as_text, \ - .compare=Array$compare, \ - .equal=Array$equal, \ - .hash=Array$hash, \ - .is_none=Array$is_none, \ - .serialize=Array$serialize, \ - .deserialize=Array$deserialize, \ -} - -#define Array$info(item_info) &((TypeInfo_t){.size=sizeof(Array_t), .align=__alignof__(Array_t), \ - .tag=ArrayInfo, .ArrayInfo.item=item_info, \ - .metamethods=Array$metamethods}) - -// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/c_strings.c b/src/stdlib/c_strings.c index 9d0f2819..dc777a7c 100644 --- a/src/stdlib/c_strings.c +++ b/src/stdlib/c_strings.c @@ -59,7 +59,7 @@ static void CString$serialize(const void *obj, FILE *out, Table_t *pointers, con fwrite(str, sizeof(char), (size_t)len, out); } -static void CString$deserialize(FILE *in, void *out, Array_t *pointers, const TypeInfo_t *) +static void CString$deserialize(FILE *in, void *out, List_t *pointers, const TypeInfo_t *) { int64_t len = -1; Int64$deserialize(in, &len, pointers, &Int64$info); diff --git a/src/stdlib/datatypes.h b/src/stdlib/datatypes.h index 6f3b7676..950c9457 100644 --- a/src/stdlib/datatypes.h +++ b/src/stdlib/datatypes.h @@ -1,22 +1,22 @@ #pragma once -// Common datastructures (arrays, tables, closures) +// Common datastructures (lists, tables, closures) #include <gmp.h> #include <stdbool.h> #include <stdint.h> #include <time.h> -#define ARRAY_LENGTH_BITS 42 -#define ARRAY_FREE_BITS 6 -#define ARRAY_REFCOUNT_BITS 3 -#define ARRAY_STRIDE_BITS 12 +#define LIST_LENGTH_BITS 42 +#define LIST_FREE_BITS 6 +#define LIST_REFCOUNT_BITS 3 +#define LIST_STRIDE_BITS 12 #define MAX_FOR_N_BITS(N) ((1<<(N))-1) -#define ARRAY_MAX_STRIDE MAX_FOR_N_BITS(ARRAY_STRIDE_BITS-1) -#define ARRAY_MIN_STRIDE (~MAX_FOR_N_BITS(ARRAY_STRIDE_BITS-1)) -#define ARRAY_MAX_DATA_REFCOUNT MAX_FOR_N_BITS(ARRAY_REFCOUNT_BITS) -#define ARRAY_MAX_FREE_ENTRIES MAX_FOR_N_BITS(ARRAY_FREE_BITS) +#define LIST_MAX_STRIDE MAX_FOR_N_BITS(LIST_STRIDE_BITS-1) +#define LIST_MIN_STRIDE (~MAX_FOR_N_BITS(LIST_STRIDE_BITS-1)) +#define LIST_MAX_DATA_REFCOUNT MAX_FOR_N_BITS(LIST_REFCOUNT_BITS) +#define LIST_MAX_FREE_ENTRIES MAX_FOR_N_BITS(LIST_FREE_BITS) #define Num_t double #define Num32_t float @@ -37,16 +37,16 @@ typedef union { typedef struct { void *data; - // All of the following fields add up to 64 bits, which means that array + // All of the following fields add up to 64 bits, which means that list // structs can be passed in two 64-bit registers. C will handle doing the // bit arithmetic to extract the necessary values, which is cheaper than // spilling onto the stack and needing to retrieve data from the stack. - int64_t length:ARRAY_LENGTH_BITS; - uint8_t free:ARRAY_FREE_BITS; + int64_t length:LIST_LENGTH_BITS; + uint8_t free:LIST_FREE_BITS; bool atomic:1; - uint8_t data_refcount:ARRAY_REFCOUNT_BITS; - int16_t stride:ARRAY_STRIDE_BITS; -} Array_t; + uint8_t data_refcount:LIST_REFCOUNT_BITS; + int16_t stride:LIST_STRIDE_BITS; +} List_t; typedef struct { uint32_t occupied:1, index:31; @@ -63,7 +63,7 @@ typedef struct { } bucket_info_t; typedef struct table_s { - Array_t entries; + List_t entries; uint64_t hash; bucket_info_t *bucket_info; struct table_s *fallback; @@ -101,12 +101,12 @@ typedef struct { typedef struct { PathType_t type; - Array_t components; + List_t components; } Path_t; #define OptionalPath_t Path_t #define OptionalBool_t uint8_t -#define OptionalArray_t Array_t +#define OptionalList_t List_t #define OptionalTable_t Table_t #define OptionalText_t Text_t #define OptionalClosure_t Closure_t diff --git a/src/stdlib/enums.c b/src/stdlib/enums.c index b66a1711..bcf47d8e 100644 --- a/src/stdlib/enums.c +++ b/src/stdlib/enums.c @@ -3,7 +3,7 @@ #include <stdint.h> #include <string.h> -#include "arrays.h" +#include "lists.h" #include "bools.h" #include "functiontype.h" #include "integers.h" @@ -102,7 +102,7 @@ public void Enum$serialize(const void *obj, FILE *out, Table_t *pointers, const } } -public void Enum$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type) +public void Enum$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) { int32_t tag = 0; Int32$deserialize(in, &tag, pointers, &Int32$info); diff --git a/src/stdlib/enums.h b/src/stdlib/enums.h index b5427711..8345c527 100644 --- a/src/stdlib/enums.h +++ b/src/stdlib/enums.h @@ -13,7 +13,7 @@ PUREFUNC bool Enum$equal(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC Text_t Enum$as_text(const void *obj, bool colorize, const TypeInfo_t *type); PUREFUNC bool Enum$is_none(const void *obj, const TypeInfo_t *type); void Enum$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type); -void Enum$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type); +void Enum$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type); #define Enum$metamethods { \ .as_text=Enum$as_text, \ diff --git a/src/stdlib/integers.c b/src/stdlib/integers.c index 8086239d..7943caee 100644 --- a/src/stdlib/integers.c +++ b/src/stdlib/integers.c @@ -9,7 +9,7 @@ #include <stdio.h> #include <stdlib.h> -#include "arrays.h" +#include "lists.h" #include "datatypes.h" #include "integers.h" #include "optionals.h" @@ -480,7 +480,7 @@ static void Int$serialize(const void *obj, FILE *out, Table_t *pointers, const T } } -static void Int$deserialize(FILE *in, void *obj, Array_t *pointers, const TypeInfo_t*) +static void Int$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t*) { if (fgetc(in) == 0) { int64_t i = 0; @@ -519,7 +519,7 @@ public void Int64$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo fputc((uint8_t)z, out); } -public void Int64$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*) +public void Int64$deserialize(FILE *in, void *outval, List_t*, const TypeInfo_t*) { uint64_t z = 0; for(size_t shift = 0; ; shift += 7) { @@ -541,7 +541,7 @@ public void Int32$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo fputc((uint8_t)z, out); } -public void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*) +public void Int32$deserialize(FILE *in, void *outval, List_t*, const TypeInfo_t*) { uint32_t z = 0; for(size_t shift = 0; ; shift += 7) { @@ -580,14 +580,14 @@ public void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t Int_t as_int = Int$from_int64((int64_t)i); \ return Int$octal(as_int, digits_int, prefix); \ } \ - public Array_t KindOfInt ## $bits(c_type x) { \ - Array_t bit_array = (Array_t){.data=GC_MALLOC_ATOMIC(sizeof(bool[8*sizeof(c_type)])), .atomic=1, .stride=sizeof(bool), .length=8*sizeof(c_type)}; \ - bool *bits = bit_array.data + sizeof(c_type)*8; \ + public List_t KindOfInt ## $bits(c_type x) { \ + List_t bit_list = (List_t){.data=GC_MALLOC_ATOMIC(sizeof(bool[8*sizeof(c_type)])), .atomic=1, .stride=sizeof(bool), .length=8*sizeof(c_type)}; \ + bool *bits = bit_list.data + sizeof(c_type)*8; \ for (size_t i = 0; i < 8*sizeof(c_type); i++) { \ *(bits--) = x & 1; \ x >>= 1; \ } \ - return bit_array; \ + return bit_list; \ } \ typedef struct { \ Optional##KindOfInt##_t current, last; \ diff --git a/src/stdlib/integers.h b/src/stdlib/integers.h index 43fc2217..8988e7c8 100644 --- a/src/stdlib/integers.h +++ b/src/stdlib/integers.h @@ -29,7 +29,7 @@ Text_t type_name ## $format(c_type i, Int_t digits); \ Text_t type_name ## $hex(c_type i, Int_t digits, bool uppercase, bool prefix); \ Text_t type_name ## $octal(c_type i, Int_t digits, bool prefix); \ - Array_t type_name ## $bits(c_type x); \ + List_t type_name ## $bits(c_type x); \ Closure_t type_name ## $to(c_type first, c_type last, Optional ## type_name ## _t step); \ Closure_t type_name ## $onward(c_type first, c_type step); \ PUREFUNC Optional ## type_name ## _t type_name ## $parse(Text_t text); \ @@ -84,9 +84,9 @@ DEFINE_INT_TYPE(int8_t, Int8) #define Int8$abs(...) I8(abs(__VA_ARGS__)) void Int64$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*); -void Int64$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*); +void Int64$deserialize(FILE *in, void *outval, List_t*, const TypeInfo_t*); void Int32$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*); -void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*); +void Int32$deserialize(FILE *in, void *outval, List_t*, const TypeInfo_t*); Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t *type); Text_t Int$value_as_text(Int_t i); diff --git a/src/stdlib/lists.c b/src/stdlib/lists.c new file mode 100644 index 00000000..69ac7026 --- /dev/null +++ b/src/stdlib/lists.c @@ -0,0 +1,813 @@ +// Functions that operate on lists + +#include <gc.h> +#include <stdbool.h> +#include <stdint.h> +#include <sys/param.h> + +#include "lists.h" +#include "integers.h" +#include "math.h" +#include "metamethods.h" +#include "optionals.h" +#include "tables.h" +#include "text.h" +#include "util.h" + +// Use inline version of siphash code: +#include "siphash.h" +#include "siphash-internals.h" + +PUREFUNC static INLINE int64_t get_padded_item_size(const TypeInfo_t *info) +{ + int64_t size = info->ListInfo.item->size; + if (info->ListInfo.item->align > 1 && size % info->ListInfo.item->align) + errx(1, "Item size is not padded!"); + return size; +} + +// Replace the list's .data pointer with a new pointer to a copy of the +// data that is compacted and has a stride of exactly `padded_item_size` +public void List$compact(List_t *list, int64_t padded_item_size) +{ + void *copy = NULL; + if (list->length > 0) { + copy = list->atomic ? GC_MALLOC_ATOMIC((size_t)list->length * (size_t)padded_item_size) + : GC_MALLOC((size_t)list->length * (size_t)padded_item_size); + if ((int64_t)list->stride == padded_item_size) { + memcpy(copy, list->data, (size_t)list->length * (size_t)padded_item_size); + } else { + for (int64_t i = 0; i < list->length; i++) + memcpy(copy + i*padded_item_size, list->data + list->stride*i, (size_t)padded_item_size); + } + } + *list = (List_t){ + .data=copy, + .length=list->length, + .stride=padded_item_size, + .atomic=list->atomic, + }; +} + +public void List$insert(List_t *list, const void *item, Int_t int_index, int64_t padded_item_size) +{ + int64_t index = Int64$from_int(int_index, false); + if (index <= 0) index = list->length + index + 1; + + if (index < 1) index = 1; + else if (index > (int64_t)list->length + 1) + fail("Invalid insertion index ", index, " for a list with length ", (int64_t)list->length); + + if (!list->data) { + list->free = 4; + list->data = list->atomic ? GC_MALLOC_ATOMIC((size_t)list->free * (size_t)padded_item_size) + : GC_MALLOC((size_t)list->free * (size_t)padded_item_size); + list->stride = padded_item_size; + } else if (list->free < 1 || list->data_refcount != 0 || (int64_t)list->stride != padded_item_size) { + // Resize policy: +50% growth (clamped between 8 and LIST_MAX_FREE_ENTRIES) + list->free = MIN(LIST_MAX_FREE_ENTRIES, MAX(8, list->length)/2); + void *copy = list->atomic ? GC_MALLOC_ATOMIC((size_t)(list->length + list->free) * (size_t)padded_item_size) + : GC_MALLOC((size_t)(list->length + list->free) * (size_t)padded_item_size); + for (int64_t i = 0; i < index-1; i++) + memcpy(copy + i*padded_item_size, list->data + list->stride*i, (size_t)padded_item_size); + for (int64_t i = index-1; i < (int64_t)list->length; i++) + memcpy(copy + (i+1)*padded_item_size, list->data + list->stride*i, (size_t)padded_item_size); + list->data = copy; + list->data_refcount = 0; + list->stride = padded_item_size; + } else { + if (index != list->length+1) + memmove( + list->data + index*padded_item_size, + list->data + (index-1)*padded_item_size, + (size_t)((list->length - index + 1)*padded_item_size)); + } + assert(list->free > 0); + --list->free; + ++list->length; + memcpy((void*)list->data + (index-1)*padded_item_size, item, (size_t)padded_item_size); +} + +public void List$insert_all(List_t *list, List_t to_insert, Int_t int_index, int64_t padded_item_size) +{ + int64_t index = Int64$from_int(int_index, false); + if (to_insert.length == 0) + return; + + if (!list->data) { + *list = to_insert; + LIST_INCREF(*list); + return; + } + + if (index < 1) index = list->length + index + 1; + + if (index < 1) index = 1; + else if (index > (int64_t)list->length + 1) + fail("Invalid insertion index ", index, " for a list with length ", (int64_t)list->length); + + if ((int64_t)list->free >= (int64_t)to_insert.length // Adequate free space + && list->data_refcount == 0 // Not aliased memory + && (int64_t)list->stride == padded_item_size) { // Contiguous list + // If we can fit this within the list's preallocated free space, do that: + list->free -= to_insert.length; + list->length += to_insert.length; + if (index != list->length+1) + memmove((void*)list->data + index*padded_item_size, + list->data + (index-1)*padded_item_size, + (size_t)((list->length - index + to_insert.length-1)*padded_item_size)); + for (int64_t i = 0; i < to_insert.length; i++) + memcpy((void*)list->data + (index-1 + i)*padded_item_size, + to_insert.data + i*to_insert.stride, (size_t)padded_item_size); + } else { + // Otherwise, allocate a new chunk of memory for the list and populate it: + int64_t new_len = list->length + to_insert.length; + list->free = MIN(LIST_MAX_FREE_ENTRIES, MAX(8, new_len/4)); + void *data = list->atomic ? GC_MALLOC_ATOMIC((size_t)((new_len + list->free) * padded_item_size)) + : GC_MALLOC((size_t)((new_len + list->free) * padded_item_size)); + void *p = data; + + // Copy first chunk of `list` if needed: + if (index > 1) { + if (list->stride == padded_item_size) { + memcpy(p, list->data, (size_t)((index-1)*padded_item_size)); + p += (index-1)*padded_item_size; + } else { + for (int64_t i = 0; i < index-1; i++) { + memcpy(p, list->data + list->stride*i, (size_t)padded_item_size); + p += padded_item_size; + } + } + } + + // Copy `to_insert` + if (to_insert.stride == padded_item_size) { + memcpy(p, to_insert.data, (size_t)(to_insert.length*padded_item_size)); + p += to_insert.length*padded_item_size; + } else { + for (int64_t i = 0; i < index-1; i++) { + memcpy(p, to_insert.data + to_insert.stride*i, (size_t)padded_item_size); + p += padded_item_size; + } + } + + // Copy last chunk of `list` if needed: + if (index < list->length + 1) { + if (list->stride == padded_item_size) { + memcpy(p, list->data + padded_item_size*(index-1), (size_t)((list->length - index + 1)*padded_item_size)); + p += (list->length - index + 1)*padded_item_size; + } else { + for (int64_t i = index-1; i < list->length-1; i++) { + memcpy(p, list->data + list->stride*i, (size_t)padded_item_size); + p += padded_item_size; + } + } + } + list->length = new_len; + list->stride = padded_item_size; + list->data = data; + list->data_refcount = 0; + } +} + +public void List$remove_at(List_t *list, Int_t int_index, Int_t int_count, int64_t padded_item_size) +{ + int64_t index = Int64$from_int(int_index, false); + if (index < 1) index = list->length + index + 1; + + int64_t count = Int64$from_int(int_count, false); + if (index < 1 || index > (int64_t)list->length || count < 1) return; + + if (count > list->length - index + 1) + count = (list->length - index) + 1; + + if (index == 1) { + list->data += list->stride * count; + } else if (index + count > list->length) { + list->free += count; + } else if (list->data_refcount != 0 || (int64_t)list->stride != padded_item_size) { + void *copy = list->atomic ? GC_MALLOC_ATOMIC((size_t)((list->length-1) * padded_item_size)) + : GC_MALLOC((size_t)((list->length-1) * padded_item_size)); + for (int64_t src = 1, dest = 1; src <= (int64_t)list->length; src++) { + if (src < index || src >= index + count) { + memcpy(copy + (dest - 1)*padded_item_size, list->data + list->stride*(src - 1), (size_t)padded_item_size); + ++dest; + } + } + list->data = copy; + list->free = 0; + list->data_refcount = 0; + } else { + memmove((void*)list->data + (index-1)*padded_item_size, list->data + (index-1 + count)*padded_item_size, + (size_t)((list->length - index + count - 1)*padded_item_size)); + list->free += count; + } + list->length -= count; + if (list->length == 0) list->data = NULL; +} + +public void List$remove_item(List_t *list, void *item, Int_t max_removals, const TypeInfo_t *type) +{ + int64_t padded_item_size = get_padded_item_size(type); + const Int_t ZERO = (Int_t){.small=(0<<2)|1}; + const Int_t ONE = (Int_t){.small=(1<<2)|1}; + const TypeInfo_t *item_type = type->ListInfo.item; + for (int64_t i = 0; i < list->length; ) { + if (max_removals.small == ZERO.small) // zero + break; + + if (generic_equal(item, list->data + i*list->stride, item_type)) { + List$remove_at(list, I(i+1), ONE, padded_item_size); + max_removals = Int$minus(max_removals, ONE); + } else { + i++; + } + } +} + +public OptionalInt_t List$find(List_t list, void *item, const TypeInfo_t *type) +{ + const TypeInfo_t *item_type = type->ListInfo.item; + for (int64_t i = 0; i < list.length; i++) { + if (generic_equal(item, list.data + i*list.stride, item_type)) + return I(i+1); + } + return NONE_INT; +} + +public OptionalInt_t List$first(List_t list, Closure_t predicate) +{ + bool (*is_good)(void*, void*) = (void*)predicate.fn; + for (int64_t i = 0; i < list.length; i++) { + if (is_good(list.data + i*list.stride, predicate.userdata)) + return I(i+1); + } + return NONE_INT; +} + +static Closure_t _sort_comparison = {.fn=NULL}; + +int _compare_closure(const void *a, const void *b) +{ + typedef int (*comparison_t)(const void*, const void*, void*); + return ((comparison_t)_sort_comparison.fn)(a, b, _sort_comparison.userdata); +} + +public void List$sort(List_t *list, Closure_t comparison, int64_t padded_item_size) +{ + if (list->data_refcount != 0 || (int64_t)list->stride != padded_item_size) + List$compact(list, padded_item_size); + + _sort_comparison = comparison; + qsort(list->data, (size_t)list->length, (size_t)padded_item_size, _compare_closure); +} + +public List_t List$sorted(List_t list, Closure_t comparison, int64_t padded_item_size) +{ + List$compact(&list, padded_item_size); + _sort_comparison = comparison; + qsort(list.data, (size_t)list.length, (size_t)padded_item_size, _compare_closure); + return list; +} + +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) +static ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) { + (void)flags; + arc4random_buf(buf, buflen); + return buflen; +} +#elif defined(__linux__) +// Use getrandom() +# include <sys/random.h> +#else + #error "Unsupported platform for secure random number generation" +#endif + +static int64_t _default_random_int64(int64_t min, int64_t max, void *userdata) +{ + (void)userdata; + if (min > max) fail("Random minimum value (", min, ") is larger than the maximum value (", max, ")"); + if (min == max) return min; + uint64_t range = (uint64_t)max - (uint64_t)min + 1; + uint64_t min_r = -range % range; + uint64_t r; + for (;;) { + getrandom(&r, sizeof(r), 0); + if (r >= min_r) break; + } + return (int64_t)((uint64_t)min + (r % range)); +} + +public void List$shuffle(List_t *list, OptionalClosure_t random_int64, int64_t padded_item_size) +{ + if (list->data_refcount != 0 || (int64_t)list->stride != padded_item_size) + List$compact(list, padded_item_size); + + typedef int64_t (*rng_fn_t)(int64_t, int64_t, void*); + rng_fn_t rng_fn = random_int64.fn ? (rng_fn_t)random_int64.fn : _default_random_int64; + char tmp[padded_item_size]; + for (int64_t i = list->length-1; i > 1; i--) { + int64_t j = rng_fn(0, i, random_int64.userdata); + if unlikely (j < 0 || j > list->length-1) + fail("The provided random number function returned an invalid value: ", j, " (not between 0 and ", i, ")"); + memcpy(tmp, list->data + i*padded_item_size, (size_t)padded_item_size); + memcpy((void*)list->data + i*padded_item_size, list->data + j*padded_item_size, (size_t)padded_item_size); + memcpy((void*)list->data + j*padded_item_size, tmp, (size_t)padded_item_size); + } +} + +public List_t List$shuffled(List_t list, Closure_t random_int64, int64_t padded_item_size) +{ + List$compact(&list, padded_item_size); + List$shuffle(&list, random_int64, padded_item_size); + return list; +} + +public void *List$random(List_t list, OptionalClosure_t random_int64) +{ + if (list.length == 0) + return NULL; // fail("Cannot get a random item from an empty list!"); + + typedef int64_t (*rng_fn_t)(int64_t, int64_t, void*); + rng_fn_t rng_fn = random_int64.fn ? (rng_fn_t)random_int64.fn : _default_random_int64; + int64_t index = rng_fn(0, list.length-1, random_int64.userdata); + if unlikely (index < 0 || index > list.length-1) + fail("The provided random number function returned an invalid value: ", index, " (not between 0 and ", (int64_t)list.length, ")"); + return list.data + list.stride*index; +} + +public Table_t List$counts(List_t list, const TypeInfo_t *type) +{ + Table_t counts = {}; + const TypeInfo_t count_type = *Table$info(type->ListInfo.item, &Int$info); + for (int64_t i = 0; i < list.length; i++) { + void *key = list.data + i*list.stride; + int64_t *count = Table$get(counts, key, &count_type); + int64_t val = count ? *count + 1 : 1; + Table$set(&counts, key, &val, &count_type); + } + return counts; +} + +static double _default_random_num(void *userdata) +{ + (void)userdata; + union { + Num_t num; + uint64_t bits; + } r = {.bits=0}, one = {.num=1.0}; + getrandom((uint8_t*)&r, sizeof(r), 0); + + // Set r.num to 1.<random-bits> + r.bits &= ~(0xFFFULL << 52); + r.bits |= (one.bits & (0xFFFULL << 52)); + return r.num - 1.0; +} + +public List_t List$sample(List_t list, Int_t int_n, List_t weights, OptionalClosure_t random_num, int64_t padded_item_size) +{ + int64_t n = Int64$from_int(int_n, false); + if (n < 0) + fail("Cannot select a negative number of values"); + + if (n == 0) + return (List_t){}; + + if (list.length == 0) + fail("There are no elements in this list!"); + + if (weights.length != list.length) + fail("List has ", (int64_t)list.length, " elements, but there are ", (int64_t)weights.length, " weights given"); + + double total = 0.0; + for (int64_t i = 0; i < weights.length && i < list.length; i++) { + double weight = *(double*)(weights.data + weights.stride*i); + if (isinf(weight)) + fail("Infinite weight!"); + else if (isnan(weight)) + fail("NaN weight!"); + else if (weight < 0.0) + fail("Negative weight!"); + else + total += weight; + } + + if (isinf(total)) + fail("Sample weights have overflowed to infinity"); + + if (total == 0.0) + fail("None of the given weights are nonzero"); + + double inverse_average = (double)list.length / total; + + struct { + int64_t alias; + double odds; + } aliases[list.length]; + + for (int64_t i = 0; i < list.length; i++) { + double weight = i >= weights.length ? 0.0 : *(double*)(weights.data + weights.stride*i); + aliases[i].odds = weight * inverse_average; + aliases[i].alias = -1; + } + + int64_t small = 0; + for (int64_t big = 0; big < list.length; big++) { + while (aliases[big].odds >= 1.0) { + while (small < list.length && (aliases[small].odds >= 1.0 || aliases[small].alias != -1)) + ++small; + + if (small >= list.length) { + aliases[big].odds = 1.0; + aliases[big].alias = big; + break; + } + + aliases[small].alias = big; + aliases[big].odds = (aliases[small].odds + aliases[big].odds) - 1.0; + } + if (big < small) small = big; + } + + for (int64_t i = small; i < list.length; i++) + if (aliases[i].alias == -1) + aliases[i].alias = i; + + typedef double (*rng_fn_t)(void*); + rng_fn_t rng_fn = random_num.fn ? (rng_fn_t)random_num.fn : _default_random_num; + + List_t selected = { + .data=list.atomic ? GC_MALLOC_ATOMIC((size_t)(n * padded_item_size)) : GC_MALLOC((size_t)(n * padded_item_size)), + .length=n, + .stride=padded_item_size, .atomic=list.atomic}; + for (int64_t i = 0; i < n; i++) { + double r = rng_fn(random_num.userdata); + if unlikely (r < 0.0 || r >= 1.0) + fail("The random number function returned a value not between 0.0 (inclusive) and 1.0 (exclusive): ", r); + r *= (double)list.length; + int64_t index = (int64_t)r; + assert(index >= 0 && index < list.length); + if ((r - (double)index) > aliases[index].odds) + index = aliases[index].alias; + memcpy(selected.data + i*selected.stride, list.data + index*list.stride, (size_t)padded_item_size); + } + return selected; +} + +public List_t List$from(List_t list, Int_t first) +{ + return List$slice(list, first, I_small(-1)); +} + +public List_t List$to(List_t list, Int_t last) +{ + return List$slice(list, I_small(1), last); +} + +public List_t List$by(List_t list, Int_t int_stride, int64_t padded_item_size) +{ + int64_t stride = Int64$from_int(int_stride, false); + // In the unlikely event that the stride value would be too large to fit in + // a 15-bit integer, fall back to creating a copy of the list: + if (unlikely(list.stride*stride < LIST_MIN_STRIDE || list.stride*stride > LIST_MAX_STRIDE)) { + void *copy = NULL; + int64_t len = (stride < 0 ? list.length / -stride : list.length / stride) + ((list.length % stride) != 0); + if (len > 0) { + copy = list.atomic ? GC_MALLOC_ATOMIC((size_t)(len * padded_item_size)) : GC_MALLOC((size_t)(len * padded_item_size)); + void *start = (stride < 0 ? list.data + (list.stride * (list.length - 1)) : list.data); + for (int64_t i = 0; i < len; i++) + memcpy(copy + i*padded_item_size, start + list.stride*stride*i, (size_t)padded_item_size); + } + return (List_t){ + .data=copy, + .length=len, + .stride=padded_item_size, + .atomic=list.atomic, + }; + } + + if (stride == 0) + return (List_t){.atomic=list.atomic}; + + return (List_t){ + .atomic=list.atomic, + .data=(stride < 0 ? list.data + (list.stride * (list.length - 1)) : list.data), + .length=(stride < 0 ? list.length / -stride : list.length / stride) + ((list.length % stride) != 0), + .stride=list.stride * stride, + .data_refcount=list.data_refcount, + }; +} + +public List_t List$slice(List_t list, Int_t int_first, Int_t int_last) + +{ + int64_t first = Int64$from_int(int_first, false); + if (first < 0) + first = list.length + first + 1; + + int64_t last = Int64$from_int(int_last, false); + if (last < 0) + last = list.length + last + 1; + + if (last > list.length) + last = list.length; + + if (first < 1 || first > list.length || last == 0) + return (List_t){.atomic=list.atomic}; + + return (List_t){ + .atomic=list.atomic, + .data=list.data + list.stride*(first-1), + .length=last - first + 1, + .stride=list.stride, + .data_refcount=list.data_refcount, + }; +} + +public List_t List$reversed(List_t list, int64_t padded_item_size) +{ + // Just in case negating the stride gives a value that doesn't fit into a + // 15-bit integer, fall back to List$by()'s more general method of copying + // the list. This should only happen if list.stride is MIN_STRIDE to + // begin with (very unlikely). + if (unlikely(-list.stride < LIST_MIN_STRIDE || -list.stride > LIST_MAX_STRIDE)) + return List$by(list, I(-1), padded_item_size); + + List_t reversed = list; + reversed.stride = -list.stride; + reversed.data = list.data + (list.length-1)*list.stride; + return reversed; +} + +public List_t List$concat(List_t x, List_t y, int64_t padded_item_size) +{ + void *data = x.atomic ? GC_MALLOC_ATOMIC((size_t)(padded_item_size*(x.length + y.length))) + : GC_MALLOC((size_t)(padded_item_size*(x.length + y.length))); + if (x.stride == padded_item_size) { + memcpy(data, x.data, (size_t)(padded_item_size*x.length)); + } else { + for (int64_t i = 0; i < x.length; i++) + memcpy(data + i*padded_item_size, x.data + i*padded_item_size, (size_t)padded_item_size); + } + + void *dest = data + padded_item_size*x.length; + if (y.stride == padded_item_size) { + memcpy(dest, y.data, (size_t)(padded_item_size*y.length)); + } else { + for (int64_t i = 0; i < y.length; i++) + memcpy(dest + i*padded_item_size, y.data + i*y.stride, (size_t)padded_item_size); + } + + return (List_t){ + .data=data, + .length=x.length + y.length, + .stride=padded_item_size, + .atomic=x.atomic, + }; +} + +public bool List$has(List_t list, void *item, const TypeInfo_t *type) +{ + const TypeInfo_t *item_type = type->ListInfo.item; + for (int64_t i = 0; i < list.length; i++) { + if (generic_equal(list.data + i*list.stride, item, item_type)) + return true; + } + return false; +} + +public void List$clear(List_t *list) +{ + *list = (List_t){.data=0, .length=0}; +} + +public int32_t List$compare(const void *vx, const void *vy, const TypeInfo_t *type) +{ + const List_t *x = (List_t*)vx, *y = (List_t*)vy; + // Early out for lists with the same data, e.g. two copies of the same list: + if (x->data == y->data && x->stride == y->stride) + return (x->length > y->length) - (x->length < y->length); + + const TypeInfo_t *item = type->ListInfo.item; + if (item->tag == PointerInfo || !item->metamethods.compare) { // data comparison + int64_t item_padded_size = type->ListInfo.item->size; + if (type->ListInfo.item->align > 1 && item_padded_size % type->ListInfo.item->align) + errx(1, "Item size is not padded!"); + + if ((int64_t)x->stride == item_padded_size && (int64_t)y->stride == item_padded_size && item->size == item_padded_size) { + int32_t cmp = (int32_t)memcmp(x->data, y->data, (size_t)(MIN(x->length, y->length)*item_padded_size)); + if (cmp != 0) return cmp; + } else { + for (int32_t i = 0, len = MIN(x->length, y->length); i < len; i++) { + int32_t cmp = (int32_t)memcmp(x->data+ x->stride*i, y->data + y->stride*i, (size_t)(item->size)); + if (cmp != 0) return cmp; + } + } + } else { + for (int32_t i = 0, len = MIN(x->length, y->length); i < len; i++) { + int32_t cmp = generic_compare(x->data + x->stride*i, y->data + y->stride*i, item); + if (cmp != 0) return cmp; + } + } + return (x->length > y->length) - (x->length < y->length); +} + +public bool List$equal(const void *x, const void *y, const TypeInfo_t *type) +{ + return x == y || (((List_t*)x)->length == ((List_t*)y)->length && List$compare(x, y, type) == 0); +} + +public Text_t List$as_text(const void *obj, bool colorize, const TypeInfo_t *type) +{ + List_t *list = (List_t*)obj; + if (!list) + return Text$concat(Text("["), generic_as_text(NULL, false, type->ListInfo.item), Text("]")); + + const TypeInfo_t *item_type = type->ListInfo.item; + Text_t text = Text("["); + for (int64_t i = 0; i < list->length; i++) { + if (i > 0) + text = Text$concat(text, Text(", ")); + Text_t item_text = generic_as_text(list->data + i*list->stride, colorize, item_type); + text = Text$concat(text, item_text); + } + text = Text$concat(text, Text("]")); + return text; +} + +public uint64_t List$hash(const void *obj, const TypeInfo_t *type) +{ + const List_t *list = (List_t*)obj; + const TypeInfo_t *item = type->ListInfo.item; + siphash sh; + siphashinit(&sh, sizeof(uint64_t[list->length])); + if (item->tag == PointerInfo || (!item->metamethods.hash && item->size == sizeof(void*))) { // Raw data hash + for (int64_t i = 0; i < list->length; i++) + siphashadd64bits(&sh, (uint64_t)(list->data + i*list->stride)); + } else { + for (int64_t i = 0; i < list->length; i++) { + uint64_t item_hash = generic_hash(list->data + i*list->stride, item); + siphashadd64bits(&sh, item_hash); + } + } + return siphashfinish_last_part(&sh, 0); +} + +static void siftdown(List_t *heap, int64_t startpos, int64_t pos, Closure_t comparison, int64_t padded_item_size) +{ + assert(pos > 0 && pos < heap->length); + char newitem[padded_item_size]; + memcpy(newitem, heap->data + heap->stride*pos, (size_t)(padded_item_size)); + while (pos > startpos) { + int64_t parentpos = (pos - 1) >> 1; + typedef int32_t (*cmp_fn_t)(void*, void*, void*); + int32_t cmp = ((cmp_fn_t)comparison.fn)(newitem, heap->data + heap->stride*parentpos, comparison.userdata); + if (cmp >= 0) + break; + + memcpy(heap->data + heap->stride*pos, heap->data + heap->stride*parentpos, (size_t)(padded_item_size)); + pos = parentpos; + } + memcpy(heap->data + heap->stride*pos, newitem, (size_t)(padded_item_size)); +} + +static void siftup(List_t *heap, int64_t pos, Closure_t comparison, int64_t padded_item_size) +{ + int64_t endpos = heap->length; + int64_t startpos = pos; + assert(pos < endpos); + + char old_top[padded_item_size]; + memcpy(old_top, heap->data + heap->stride*pos, (size_t)(padded_item_size)); + // Bubble up the smallest leaf node + int64_t limit = endpos >> 1; + while (pos < limit) { + int64_t childpos = 2*pos + 1; // Smaller of the two child nodes + if (childpos + 1 < endpos) { + typedef int32_t (*cmp_fn_t)(void*, void*, void*); + int32_t cmp = ((cmp_fn_t)comparison.fn)( + heap->data + heap->stride*childpos, + heap->data + heap->stride*(childpos + 1), + comparison.userdata); + childpos += (cmp >= 0); + } + + // Move the child node up: + memcpy(heap->data + heap->stride*pos, heap->data + heap->stride*childpos, (size_t)(padded_item_size)); + pos = childpos; + } + memcpy(heap->data + heap->stride*pos, old_top, (size_t)(padded_item_size)); + // Shift the node's parents down: + siftdown(heap, startpos, pos, comparison, padded_item_size); +} + +public void List$heap_push(List_t *heap, const void *item, Closure_t comparison, int64_t padded_item_size) +{ + List$insert(heap, item, I(0), padded_item_size); + + if (heap->length > 1) { + if (heap->data_refcount != 0) + List$compact(heap, padded_item_size); + siftdown(heap, 0, heap->length-1, comparison, padded_item_size); + } +} + +public void List$heap_pop(List_t *heap, Closure_t comparison, int64_t padded_item_size) +{ + if (heap->length == 0) + fail("Attempt to pop from an empty list"); + + if (heap->length == 1) { + *heap = (List_t){}; + } else if (heap->length == 2) { + heap->data += heap->stride; + --heap->length; + } else { + if (heap->data_refcount != 0) + List$compact(heap, padded_item_size); + memcpy(heap->data, heap->data + heap->stride*(heap->length-1), (size_t)(padded_item_size)); + --heap->length; + siftup(heap, 0, comparison, padded_item_size); + } +} + +public void List$heapify(List_t *heap, Closure_t comparison, int64_t padded_item_size) +{ + if (heap->data_refcount != 0) + List$compact(heap, padded_item_size); + + // It's necessary to bump the refcount because the user's comparison + // function could do stuff that modifies the heap's data. + LIST_INCREF(*heap); + int64_t i, n = heap->length; + for (i = (n >> 1) - 1 ; i >= 0 ; i--) + siftup(heap, i, comparison, padded_item_size); + LIST_DECREF(*heap); +} + +public Int_t List$binary_search(List_t list, void *target, Closure_t comparison) +{ + typedef int32_t (*cmp_fn_t)(void*, void*, void*); + int64_t lo = 0, hi = list.length-1; + while (lo <= hi) { + int64_t mid = (lo + hi) / 2; + int32_t cmp = ((cmp_fn_t)comparison.fn)( + list.data + list.stride*mid, target, comparison.userdata); + if (cmp == 0) + return I(mid+1); + else if (cmp < 0) + lo = mid + 1; + else if (cmp > 0) + hi = mid - 1; + } + return I(lo+1); // Return the index where the target would be inserted +} + +public PUREFUNC bool List$is_none(const void *obj, const TypeInfo_t*) +{ + return ((List_t*)obj)->length < 0; +} + +public void List$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) +{ + List_t list = *(List_t*)obj; + int64_t len = list.length; + Int64$serialize(&len, out, pointers, &Int64$info); + auto item_serialize = type->ListInfo.item->metamethods.serialize; + if (item_serialize) { + for (int64_t i = 0; i < len; i++) + item_serialize(list.data + i*list.stride, out, pointers, type->ListInfo.item); + } else if (list.stride == type->ListInfo.item->size) { + fwrite(list.data, (size_t)type->ListInfo.item->size, (size_t)len, out); + } else { + for (int64_t i = 0; i < len; i++) + fwrite(list.data + i*list.stride, (size_t)type->ListInfo.item->size, 1, out); + } +} + +public void List$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *type) +{ + int64_t len = -1; + Int64$deserialize(in, &len, pointers, &Int64$info); + int64_t padded_size = type->ListInfo.item->size; + if (type->ListInfo.item->align > 0 && padded_size % type->ListInfo.item->align > 0) + padded_size += type->ListInfo.item->align - (padded_size % type->ListInfo.item->align); + List_t list = { + .length=len, + .data=GC_MALLOC((size_t)(len*padded_size)), + .stride=padded_size, + }; + auto item_deserialize = type->ListInfo.item->metamethods.deserialize; + if (item_deserialize) { + for (int64_t i = 0; i < len; i++) + item_deserialize(in, list.data + i*list.stride, pointers, type->ListInfo.item); + } else if (list.stride == type->ListInfo.item->size) { + fread(list.data, (size_t)type->ListInfo.item->size, (size_t)len, in); + } else { + for (int64_t i = 0; i < len; i++) + fread(list.data + i*list.stride, (size_t)type->ListInfo.item->size, 1, in); + } + *(List_t*)obj = list; +} + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/lists.h b/src/stdlib/lists.h new file mode 100644 index 00000000..a2853e48 --- /dev/null +++ b/src/stdlib/lists.h @@ -0,0 +1,137 @@ +#pragma once + +// Functions that operate on lists + +#include <stdbool.h> + +#include "datatypes.h" +#include "integers.h" +#include "types.h" +#include "util.h" + +// Convert negative indices to back-indexed without branching: index0 = index + (index < 0)*(len+1)) - 1 +#define List_get(item_type, arr_expr, index_expr, start, end) *({ \ + const List_t list = arr_expr; int64_t index = index_expr; \ + int64_t off = index + (index < 0) * (list.length + 1) - 1; \ + if (unlikely(off < 0 || off >= list.length)) \ + fail_source(__SOURCE_FILE__, start, end, "Invalid list index: ", index, " (list has length ", (int64_t)list.length, ")\n"); \ + (item_type*)(list.data + list.stride * off);}) +#define List_get_unchecked(type, x, i) *({ const List_t list = x; int64_t index = i; \ + int64_t off = index + (index < 0) * (list.length + 1) - 1; \ + (type*)(list.data + list.stride * off);}) +#define List_lvalue(item_type, arr_expr, index_expr, start, end) *({ \ + List_t *list = arr_expr; int64_t index = index_expr; \ + int64_t off = index + (index < 0) * (list->length + 1) - 1; \ + if (unlikely(off < 0 || off >= list->length)) \ + fail_source(__SOURCE_FILE__, start, end, "Invalid list index: ", index, " (list has length ", (int64_t)list->length, ")\n"); \ + if (list->data_refcount > 0) \ + List$compact(list, sizeof(item_type)); \ + (item_type*)(list->data + list->stride * off); }) +#define List_lvalue_unchecked(item_type, arr_expr, index_expr) *({ \ + List_t *list = arr_expr; int64_t index = index_expr; \ + int64_t off = index + (index < 0) * (list->length + 1) - 1; \ + if (list->data_refcount > 0) \ + List$compact(list, sizeof(item_type)); \ + (item_type*)(list->data + list->stride * off); }) +#define List_set(item_type, list, index, value, start, end) \ + List_lvalue(item_type, arr_expr, index, start, end) = value +#define is_atomic(x) _Generic(x, bool: true, int8_t: true, int16_t: true, int32_t: true, int64_t: true, float: true, double: true, default: false) +#define TypedList(t, ...) ({ t items[] = {__VA_ARGS__}; \ + (List_t){.length=sizeof(items)/sizeof(items[0]), \ + .stride=(int64_t)&items[1] - (int64_t)&items[0], \ + .data=memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ + .atomic=0, \ + .data_refcount=0}; }) +#define TypedListN(t, N, ...) ({ t items[N] = {__VA_ARGS__}; \ + (List_t){.length=N, \ + .stride=(int64_t)&items[1] - (int64_t)&items[0], \ + .data=memcpy(GC_MALLOC(sizeof(items)), items, sizeof(items)), \ + .atomic=0, \ + .data_refcount=0}; }) +#define List(x, ...) ({ __typeof(x) items[] = {x, __VA_ARGS__}; \ + (List_t){.length=sizeof(items)/sizeof(items[0]), \ + .stride=(int64_t)&items[1] - (int64_t)&items[0], \ + .data=memcpy(is_atomic(x) ? GC_MALLOC_ATOMIC(sizeof(items)) : GC_MALLOC(sizeof(items)), items, sizeof(items)), \ + .atomic=is_atomic(x), \ + .data_refcount=0}; }) +// List refcounts use a saturating add, where once it's at the max value, it stays there. +#define LIST_INCREF(list) (list).data_refcount += ((list).data_refcount < LIST_MAX_DATA_REFCOUNT) +#define LIST_DECREF(list) (list).data_refcount -= ((list).data_refcount < LIST_MAX_DATA_REFCOUNT) +#define LIST_COPY(list) ({ LIST_INCREF(list); list; }) + +#define List$insert_value(list, item_expr, index, padded_item_size) List$insert(list, (__typeof(item_expr)[1]){item_expr}, index, padded_item_size) +void List$insert(List_t *list, const void *item, Int_t index, int64_t padded_item_size); +void List$insert_all(List_t *list, List_t to_insert, Int_t index, int64_t padded_item_size); +void List$remove_at(List_t *list, Int_t index, Int_t count, int64_t padded_item_size); +void List$remove_item(List_t *list, void *item, Int_t max_removals, const TypeInfo_t *type); +#define List$remove_item_value(list, item_expr, max, type) List$remove_item(list, (__typeof(item_expr)[1]){item_expr}, max, type) + +#define List$pop(arr_expr, index_expr, item_type, nonnone_var, nonnone_expr, none_expr) ({ \ + List_t *list = arr_expr; \ + Int_t index = index_expr; \ + int64_t index64 = Int64$from_int(index, false); \ + int64_t off = index64 + (index64 < 0) * (list->length + 1) - 1; \ + (off >= 0 && off < list->length) ? ({ \ + item_type nonnone_var = *(item_type*)(list->data + off*list->stride); \ + List$remove_at(list, index, I_small(1), sizeof(item_type)); \ + nonnone_expr; \ + }) : none_expr; }) + +OptionalInt_t List$find(List_t list, void *item, const TypeInfo_t *type); +#define List$find_value(list, item_expr, type) ({ __typeof(item_expr) item = item_expr; List$find(list, &item, type); }) +OptionalInt_t List$first(List_t list, Closure_t predicate); +void List$sort(List_t *list, Closure_t comparison, int64_t padded_item_size); +List_t List$sorted(List_t list, Closure_t comparison, int64_t padded_item_size); +void List$shuffle(List_t *list, OptionalClosure_t random_int64, int64_t padded_item_size); +List_t List$shuffled(List_t list, OptionalClosure_t random_int64, int64_t padded_item_size); +void *List$random(List_t list, OptionalClosure_t random_int64); +#define List$random_value(list, random_int64, t) ({ List_t _arr = list; if (_arr.length == 0) fail("Cannot get a random value from an empty list!"); *(t*)List$random(_arr, random_int64); }) +List_t List$sample(List_t list, Int_t n, List_t weights, Closure_t random_num, int64_t padded_item_size); +Table_t List$counts(List_t list, const TypeInfo_t *type); +void List$clear(List_t *list); +void List$compact(List_t *list, int64_t padded_item_size); +PUREFUNC bool List$has(List_t list, void *item, const TypeInfo_t *type); +#define List$has_value(list, item_expr, type) ({ __typeof(item_expr) item = item_expr; List$has(list, &item, type); }) +PUREFUNC List_t List$from(List_t list, Int_t first); +PUREFUNC List_t List$to(List_t list, Int_t last); +PUREFUNC List_t List$by(List_t list, Int_t stride, int64_t padded_item_size); +PUREFUNC List_t List$slice(List_t list, Int_t int_first, Int_t int_last); +PUREFUNC List_t List$reversed(List_t list, int64_t padded_item_size); +List_t List$concat(List_t x, List_t y, int64_t padded_item_size); +PUREFUNC uint64_t List$hash(const void *list, const TypeInfo_t *type); +PUREFUNC int32_t List$compare(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC bool List$equal(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC bool List$is_none(const void *obj, const TypeInfo_t*); +Text_t List$as_text(const void *list, bool colorize, const TypeInfo_t *type); +void List$heapify(List_t *heap, Closure_t comparison, int64_t padded_item_size); +void List$heap_push(List_t *heap, const void *item, Closure_t comparison, int64_t padded_item_size); +#define List$heap_push_value(heap, _value, comparison, padded_item_size) ({ __typeof(_value) value = _value; List$heap_push(heap, &value, comparison, padded_item_size); }) +void List$heap_pop(List_t *heap, Closure_t comparison, int64_t padded_item_size); +#define List$heap_pop_value(heap, comparison, type, nonnone_var, nonnone_expr, none_expr) \ + ({ List_t *_heap = heap; \ + (_heap->length > 0) ? ({ \ + type nonnone_var = *(type*)_heap->data; \ + List$heap_pop(_heap, comparison, sizeof(type)); \ + nonnone_expr; \ + }) : none_expr; }) +Int_t List$binary_search(List_t list, void *target, Closure_t comparison); +#define List$binary_search_value(list, target, comparison) \ + ({ __typeof(target) _target = target; List$binary_search(list, &_target, comparison); }) +void List$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type); +void List$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *type); + +#define List$metamethods { \ + .as_text=List$as_text, \ + .compare=List$compare, \ + .equal=List$equal, \ + .hash=List$hash, \ + .is_none=List$is_none, \ + .serialize=List$serialize, \ + .deserialize=List$deserialize, \ +} + +#define List$info(item_info) &((TypeInfo_t){.size=sizeof(List_t), .align=__alignof__(List_t), \ + .tag=ListInfo, .ListInfo.item=item_info, \ + .metamethods=List$metamethods}) + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/metamethods.c b/src/stdlib/metamethods.c index a7622e0b..6ee52222 100644 --- a/src/stdlib/metamethods.c +++ b/src/stdlib/metamethods.c @@ -3,7 +3,7 @@ #include <stdint.h> #include <string.h> -#include "arrays.h" +#include "lists.h" #include "bools.h" #include "bytes.h" #include "functiontype.h" @@ -61,7 +61,7 @@ public void _serialize(const void *obj, FILE *out, Table_t *pointers, const Type fwrite(obj, (size_t)type->size, 1, out); } -public Array_t generic_serialize(const void *x, const TypeInfo_t *type) +public List_t generic_serialize(const void *x, const TypeInfo_t *type) { char *buf = NULL; size_t size = 0; @@ -69,7 +69,7 @@ public Array_t generic_serialize(const void *x, const TypeInfo_t *type) Table_t pointers = {}; _serialize(x, stream, &pointers, type); fclose(stream); - Array_t bytes = { + List_t bytes = { .data=GC_MALLOC_ATOMIC(size), .length=(int64_t)size, .stride=1, @@ -80,7 +80,7 @@ public Array_t generic_serialize(const void *x, const TypeInfo_t *type) return bytes; } -public void _deserialize(FILE *input, void *outval, Array_t *pointers, const TypeInfo_t *type) +public void _deserialize(FILE *input, void *outval, List_t *pointers, const TypeInfo_t *type) { if (type->metamethods.deserialize) { type->metamethods.deserialize(input, outval, pointers, type); @@ -90,13 +90,13 @@ public void _deserialize(FILE *input, void *outval, Array_t *pointers, const Typ fread(outval, (size_t)type->size, 1, input); } -public void generic_deserialize(Array_t bytes, void *outval, const TypeInfo_t *type) +public void generic_deserialize(List_t bytes, void *outval, const TypeInfo_t *type) { if (bytes.stride != 1) - Array$compact(&bytes, 1); + List$compact(&bytes, 1); FILE *input = fmemopen(bytes.data, (size_t)bytes.length, "r"); - Array_t pointers = {}; + List_t pointers = {}; _deserialize(input, outval, &pointers, type); fclose(input); } @@ -115,7 +115,7 @@ public void cannot_serialize(const void*, FILE*, Table_t*, const TypeInfo_t *typ } __attribute__((noreturn)) -public void cannot_deserialize(FILE*, void*, Array_t*, const TypeInfo_t *type) +public void cannot_deserialize(FILE*, void*, List_t*, const TypeInfo_t *type) { Text_t typestr = generic_as_text(NULL, false, type); fail("Values of type ", typestr, " cannot be serialized or deserialized!"); diff --git a/src/stdlib/metamethods.h b/src/stdlib/metamethods.h index a75fcf7f..ca0a1e7e 100644 --- a/src/stdlib/metamethods.h +++ b/src/stdlib/metamethods.h @@ -12,11 +12,11 @@ PUREFUNC int32_t generic_compare(const void *x, const void *y, const TypeInfo_t PUREFUNC bool generic_equal(const void *x, const void *y, const TypeInfo_t *type); Text_t generic_as_text(const void *obj, bool colorize, const TypeInfo_t *type); void _serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type); -Array_t generic_serialize(const void *x, const TypeInfo_t *type); -void _deserialize(FILE *input, void *outval, Array_t *pointers, const TypeInfo_t *type); -void generic_deserialize(Array_t bytes, void *outval, const TypeInfo_t *type); +List_t generic_serialize(const void *x, const TypeInfo_t *type); +void _deserialize(FILE *input, void *outval, List_t *pointers, const TypeInfo_t *type); +void generic_deserialize(List_t bytes, void *outval, const TypeInfo_t *type); int generic_print(const void *obj, bool colorize, const TypeInfo_t *type); void cannot_serialize(const void*, FILE*, Table_t*, const TypeInfo_t *type); -void cannot_deserialize(FILE*, void*, Array_t*, const TypeInfo_t *type); +void cannot_deserialize(FILE*, void*, List_t*, const TypeInfo_t *type); // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/src/stdlib/nums.c b/src/stdlib/nums.c index ca8dcc4e..9edb8751 100644 --- a/src/stdlib/nums.c +++ b/src/stdlib/nums.c @@ -7,7 +7,7 @@ #include <stdint.h> #include <stdlib.h> -#include "arrays.h" +#include "lists.h" #include "nums.h" #include "string.h" #include "text.h" diff --git a/src/stdlib/optionals.c b/src/stdlib/optionals.c index d3309029..f33b471d 100644 --- a/src/stdlib/optionals.c +++ b/src/stdlib/optionals.c @@ -61,7 +61,7 @@ public void Optional$serialize(const void *obj, FILE *out, Table_t *pointers, co _serialize(obj, out, pointers, type->OptionalInfo.type); } -public void Optional$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type) +public void Optional$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) { bool has_value = (bool)fgetc(in); const TypeInfo_t *nonnull = type->OptionalInfo.type; @@ -71,8 +71,8 @@ public void Optional$deserialize(FILE *in, void *outval, Array_t *pointers, cons } else { if (nonnull->tag == TextInfo) *(Text_t*)outval = NONE_TEXT; - else if (nonnull->tag == ArrayInfo) - *(Array_t*)outval = (Array_t){.length=-1}; + else if (nonnull->tag == ListInfo) + *(List_t*)outval = (List_t){.length=-1}; else if (nonnull->tag == TableInfo) *(Table_t*)outval = (Table_t){.entries={.length=-1}}; else if (nonnull == &Num$info) diff --git a/src/stdlib/optionals.h b/src/stdlib/optionals.h index 8d25c5f1..2ffd5a50 100644 --- a/src/stdlib/optionals.h +++ b/src/stdlib/optionals.h @@ -10,7 +10,7 @@ #include "types.h" #include "util.h" -#define NONE_ARRAY ((Array_t){.length=-1}) +#define NONE_LIST ((List_t){.length=-1}) #define NONE_BOOL ((OptionalBool_t)2) #define NONE_INT ((OptionalInt_t){.small=0}) #define NONE_TABLE ((OptionalTable_t){.entries.length=-1}) @@ -24,7 +24,7 @@ PUREFUNC int32_t Optional$compare(const void *x, const void *y, const TypeInfo_t PUREFUNC bool Optional$equal(const void *x, const void *y, const TypeInfo_t *type); Text_t Optional$as_text(const void *obj, bool colorize, const TypeInfo_t *type); void Optional$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type); -void Optional$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type); +void Optional$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type); #define Optional$metamethods { \ .hash=Optional$hash, \ diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c index 7a5346f5..cad0e0ff 100644 --- a/src/stdlib/paths.c +++ b/src/stdlib/paths.c @@ -17,7 +17,7 @@ #include <unistd.h> #include <unistr.h> -#include "arrays.h" +#include "lists.h" #include "enums.h" #include "files.h" #include "integers.h" @@ -37,16 +37,16 @@ static const Path_t HOME_PATH = {.type.$tag=PATH_HOME}, ROOT_PATH = {.type.$tag=PATH_ABSOLUTE}, CURDIR_PATH = {.type.$tag=PATH_RELATIVE}; -static void clean_components(Array_t *components) +static void clean_components(List_t *components) { for (int64_t i = 0; i < components->length; ) { Text_t *component = (Text_t*)(components->data + i*components->stride); if (component->length == 0 || Text$equal_values(*component, Text("."))) { - Array$remove_at(components, I(i+1), I(1), sizeof(Text_t)); + List$remove_at(components, I(i+1), I(1), sizeof(Text_t)); } else if (i > 0 && Text$equal_values(*component, Text(".."))) { Text_t *prev = (Text_t*)(components->data + (i-1)*components->stride); if (!Text$equal_values(*prev, Text(".."))) { - Array$remove_at(components, I(i), I(2), sizeof(Text_t)); + List$remove_at(components, I(i), I(2), sizeof(Text_t)); i -= 1; } else { i += 1; @@ -86,10 +86,10 @@ public Path_t Path$from_str(const char *str) && result.components.length > 1 && !Text$equal_values(Text(".."), *(Text_t*)(result.components.data + result.components.stride*(result.components.length-1)))) { // Pop off /foo/baz/.. -> /foo - Array$remove_at(&result.components, I(result.components.length), I(1), sizeof(Text_t)); + List$remove_at(&result.components, I(result.components.length), I(1), sizeof(Text_t)); } else { Text_t component = Text$from_strn(str, component_len); - Array$insert_value(&result.components, component, I(0), sizeof(Text_t)); + List$insert_value(&result.components, component, I(0), sizeof(Text_t)); } str += component_len; } @@ -107,7 +107,7 @@ public Path_t Path$expand_home(Path_t path) { if (path.type.$tag == PATH_HOME) { Path_t pwd = Path$from_str(getenv("HOME")); - Array_t components = Array$concat(pwd.components, path.components, sizeof(Text_t)); + List_t components = List$concat(pwd.components, path.components, sizeof(Text_t)); assert(components.length == path.components.length + pwd.components.length); clean_components(&components); path = (Path_t){.type.$tag=PATH_ABSOLUTE, .components=components}; @@ -119,11 +119,11 @@ public Path_t Path$_concat(int n, Path_t items[n]) { assert(n > 0); Path_t result = items[0]; - ARRAY_INCREF(result.components); + LIST_INCREF(result.components); for (int i = 1; i < n; i++) { if (items[i].type.$tag != PATH_RELATIVE) fail("Cannot concatenate an absolute or home-based path onto another path: (", items[i], ")"); - Array$insert_all(&result.components, items[i].components, I(0), sizeof(Text_t)); + List$insert_all(&result.components, items[i].components, I(0), sizeof(Text_t)); } clean_components(&result.components); return result; @@ -134,8 +134,8 @@ public Path_t Path$resolved(Path_t path, Path_t relative_to) if (path.type.$tag == PATH_RELATIVE && !(relative_to.type.$tag == PATH_RELATIVE && relative_to.components.length == 0)) { Path_t result = {.type.$tag=relative_to.type.$tag}; result.components = relative_to.components; - ARRAY_INCREF(result.components); - Array$insert_all(&result.components, path.components, I(0), sizeof(Text_t)); + LIST_INCREF(result.components); + List$insert_all(&result.components, path.components, I(0), sizeof(Text_t)); clean_components(&result.components); return result; } @@ -157,11 +157,11 @@ public Path_t Path$relative_to(Path_t path, Path_t relative_to) } for (int64_t i = shared; i < relative_to.components.length; i++) - Array$insert_value(&result.components, Text(".."), I(1), sizeof(Text_t)); + List$insert_value(&result.components, Text(".."), I(1), sizeof(Text_t)); for (int64_t i = shared; i < path.components.length; i++) { Text_t *p = (Text_t*)(path.components.data + i*path.components.stride); - Array$insert(&result.components, p, I(0), sizeof(Text_t)); + List$insert(&result.components, p, I(0), sizeof(Text_t)); } //clean_components(&result.components); return result; @@ -278,7 +278,7 @@ public OptionalInt64_t Path$changed(Path_t path, bool follow_symlinks) return (OptionalInt64_t){.value=(int64_t)sb.st_ctime}; } -static void _write(Path_t path, Array_t bytes, int mode, int permissions) +static void _write(Path_t path, List_t bytes, int mode, int permissions) { path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); @@ -287,7 +287,7 @@ static void _write(Path_t path, Array_t bytes, int mode, int permissions) fail("Could not write to file: ", path_str, "\n", strerror(errno)); if (bytes.stride != 1) - Array$compact(&bytes, 1); + List$compact(&bytes, 1); ssize_t written = write(fd, bytes.data, (size_t)bytes.length); if (written != (ssize_t)bytes.length) fail("Could not write to file: ", path_str, "\n", strerror(errno)); @@ -296,36 +296,36 @@ static void _write(Path_t path, Array_t bytes, int mode, int permissions) public void Path$write(Path_t path, Text_t text, int permissions) { - Array_t bytes = Text$utf8_bytes(text); + List_t bytes = Text$utf8_bytes(text); _write(path, bytes, O_WRONLY | O_CREAT | O_TRUNC, permissions); } -public void Path$write_bytes(Path_t path, Array_t bytes, int permissions) +public void Path$write_bytes(Path_t path, List_t bytes, int permissions) { _write(path, bytes, O_WRONLY | O_CREAT | O_TRUNC, permissions); } public void Path$append(Path_t path, Text_t text, int permissions) { - Array_t bytes = Text$utf8_bytes(text); + List_t bytes = Text$utf8_bytes(text); _write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions); } -public void Path$append_bytes(Path_t path, Array_t bytes, int permissions) +public void Path$append_bytes(Path_t path, List_t bytes, int permissions) { _write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions); } -public OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t count) +public OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) { path = Path$expand_home(path); int fd = open(Path$as_c_string(path), O_RDONLY); if (fd == -1) - return NONE_ARRAY; + return NONE_LIST; struct stat sb; if (fstat(fd, &sb) != 0) - return NONE_ARRAY; + return NONE_LIST; int64_t const target_count = count.small ? Int64$from_int(count, false) : INT64_MAX; if (target_count < 0) @@ -340,7 +340,7 @@ public OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t count) if (count.small && (int64_t)sb.st_size < target_count) fail("Could not read ", target_count, " bytes from ", path, " (only got ", (uint64_t)sb.st_size, ")"); int64_t len = count.small ? target_count : (int64_t)sb.st_size; - return (Array_t){.data=content, .atomic=1, .stride=1, .length=len}; + return (List_t){.data=content, .atomic=1, .stride=1, .length=len}; } else { size_t capacity = 256, len = 0; char *content = GC_MALLOC_ATOMIC(capacity); @@ -351,7 +351,7 @@ public OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t count) ssize_t just_read = read(fd, chunk, to_read); if (just_read < 0) { close(fd); - return NONE_ARRAY; + return NONE_LIST; } else if (just_read == 0) { if (errno == EAGAIN || errno == EINTR) continue; @@ -369,13 +369,13 @@ public OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t count) close(fd); if (count.small != 0 && (int64_t)len < target_count) fail("Could not read ", target_count, " bytes from ", path, " (only got ", (uint64_t)len, ")"); - return (Array_t){.data=content, .atomic=1, .stride=1, .length=len}; + return (List_t){.data=content, .atomic=1, .stride=1, .length=len}; } } public OptionalText_t Path$read(Path_t path) { - Array_t bytes = Path$read_bytes(path, NONE_INT); + List_t bytes = Path$read_bytes(path, NONE_INT); if (bytes.length < 0) return NONE_TEXT; return Text$from_bytes(bytes); } @@ -471,11 +471,11 @@ public void Path$create_directory(Path_t path, int permissions) fail("Could not create directory: ", c_path, " (", strerror(errno), ")"); } -static Array_t _filtered_children(Path_t path, bool include_hidden, mode_t filter) +static List_t _filtered_children(Path_t path, bool include_hidden, mode_t filter) { path = Path$expand_home(path); struct dirent *dir; - Array_t children = {}; + List_t children = {}; const char *path_str = Path$as_c_string(path); size_t path_len = strlen(path_str); DIR *d = opendir(path_str); @@ -499,23 +499,23 @@ static Array_t _filtered_children(Path_t path, bool include_hidden, mode_t filte continue; Path_t child = Path$from_str(child_str); - Array$insert(&children, &child, I(0), sizeof(Path_t)); + List$insert(&children, &child, I(0), sizeof(Path_t)); } closedir(d); return children; } -public Array_t Path$children(Path_t path, bool include_hidden) +public List_t Path$children(Path_t path, bool include_hidden) { return _filtered_children(path, include_hidden, (mode_t)-1); } -public Array_t Path$files(Path_t path, bool include_hidden) +public List_t Path$files(Path_t path, bool include_hidden) { return _filtered_children(path, include_hidden, S_IFREG); } -public Array_t Path$subdirectories(Path_t path, bool include_hidden) +public List_t Path$subdirectories(Path_t path, bool include_hidden) { return _filtered_children(path, include_hidden, S_IFDIR); } @@ -535,7 +535,7 @@ public Path_t Path$unique_directory(Path_t path) return Path$from_str(created); } -public Path_t Path$write_unique_bytes(Path_t path, Array_t bytes) +public Path_t Path$write_unique_bytes(Path_t path, List_t bytes) { path = Path$expand_home(path); const char *path_str = Path$as_c_string(path); @@ -555,7 +555,7 @@ public Path_t Path$write_unique_bytes(Path_t path, Array_t bytes) fail("Could not write to unique file: ", buf, "\n", strerror(errno)); if (bytes.stride != 1) - Array$compact(&bytes, 1); + List$compact(&bytes, 1); ssize_t written = write(fd, bytes.data, (size_t)bytes.length); if (written != (ssize_t)bytes.length) @@ -575,11 +575,11 @@ public Path_t Path$parent(Path_t path) return path; } else if (path.components.length > 0 && !Text$equal_values(*(Text_t*)(path.components.data + path.components.stride*(path.components.length-1)), Text(".."))) { - return (Path_t){.type.$tag=path.type.$tag, .components=Array$slice(path.components, I(1), I(-2))}; + return (Path_t){.type.$tag=path.type.$tag, .components=List$slice(path.components, I(1), I(-2))}; } else { Path_t result = {.type.$tag=path.type.$tag, .components=path.components}; - ARRAY_INCREF(result.components); - Array$insert_value(&result.components, Text(".."), I(0), sizeof(Text_t)); + LIST_INCREF(result.components); + List$insert_value(&result.components, Text(".."), I(0), sizeof(Text_t)); return result; } } @@ -610,8 +610,8 @@ public Path_t Path$with_component(Path_t path, Text_t component) .type.$tag=path.type.$tag, .components=path.components, }; - ARRAY_INCREF(result.components); - Array$insert(&result.components, &component, I(0), sizeof(Text_t)); + LIST_INCREF(result.components); + List$insert(&result.components, &component, I(0), sizeof(Text_t)); clean_components(&result.components); return result; } @@ -625,9 +625,9 @@ public Path_t Path$with_extension(Path_t path, Text_t extension, bool replace) .type.$tag=path.type.$tag, .components=path.components, }; - ARRAY_INCREF(result.components); + LIST_INCREF(result.components); Text_t last = *(Text_t*)(path.components.data + path.components.stride*(path.components.length-1)); - Array$remove_at(&result.components, I(-1), I(1), sizeof(Text_t)); + List$remove_at(&result.components, I(-1), I(1), sizeof(Text_t)); if (replace) { const char *base = Text$as_c_string(last); const char *dot = strchr(base + 1, '.'); @@ -636,7 +636,7 @@ public Path_t Path$with_extension(Path_t path, Text_t extension, bool replace) } last = Text$concat(last, extension); - Array$insert(&result.components, &last, I(0), sizeof(Text_t)); + List$insert(&result.components, &last, I(0), sizeof(Text_t)); return result; } @@ -685,21 +685,21 @@ public OptionalClosure_t Path$by_line(Path_t path) return (Closure_t){.fn=(void*)_next_line, .userdata=wrapper}; } -public Array_t Path$glob(Path_t path) +public List_t Path$glob(Path_t path) { glob_t glob_result; int status = glob(Path$as_c_string(path), GLOB_BRACE | GLOB_TILDE, NULL, &glob_result); if (status != 0 && status != GLOB_NOMATCH) fail("Failed to perform globbing"); - Array_t glob_files = {}; + List_t glob_files = {}; for (size_t i = 0; i < glob_result.gl_pathc; i++) { size_t len = strlen(glob_result.gl_pathv[i]); if ((len >= 2 && glob_result.gl_pathv[i][len-1] == '.' && glob_result.gl_pathv[i][len-2] == '/') || (len >= 2 && glob_result.gl_pathv[i][len-1] == '.' && glob_result.gl_pathv[i][len-2] == '.' && glob_result.gl_pathv[i][len-3] == '/')) continue; Path_t p = Path$from_str(glob_result.gl_pathv[i]); - Array$insert(&glob_files, &p, I(0), sizeof(Path_t)); + List$insert(&glob_files, &p, I(0), sizeof(Path_t)); } return glob_files; } @@ -730,7 +730,7 @@ public PUREFUNC int32_t Path$compare(const void *va, const void *vb, const TypeI Path_t *a = (Path_t*)va, *b = (Path_t*)vb; int diff = ((int)a->type.$tag - (int)b->type.$tag); if (diff != 0) return diff; - return Array$compare(&a->components, &b->components, Array$info(&Text$info)); + return List$compare(&a->components, &b->components, List$info(&Text$info)); } public PUREFUNC bool Path$equal(const void *va, const void *vb, const TypeInfo_t *type) @@ -738,13 +738,13 @@ public PUREFUNC bool Path$equal(const void *va, const void *vb, const TypeInfo_t (void)type; Path_t *a = (Path_t*)va, *b = (Path_t*)vb; if (a->type.$tag != b->type.$tag) return false; - return Array$equal(&a->components, &b->components, Array$info(&Text$info)); + return List$equal(&a->components, &b->components, List$info(&Text$info)); } public PUREFUNC bool Path$equal_values(Path_t a, Path_t b) { if (a.type.$tag != b.type.$tag) return false; - return Array$equal(&a.components, &b.components, Array$info(&Text$info)); + return List$equal(&a.components, &b.components, List$info(&Text$info)); } public int Path$print(FILE *f, Path_t path) @@ -809,15 +809,15 @@ public void Path$serialize(const void *obj, FILE *out, Table_t *pointers, const (void)type; Path_t *path = (Path_t*)obj; fputc((int)path->type.$tag, out); - Array$serialize(&path->components, out, pointers, Array$info(&Text$info)); + List$serialize(&path->components, out, pointers, List$info(&Text$info)); } -public void Path$deserialize(FILE *in, void *obj, Array_t *pointers, const TypeInfo_t *type) +public void Path$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *type) { (void)type; Path_t path = {}; path.type.$tag = fgetc(in); - Array$deserialize(in, &path.components, pointers, Array$info(&Text$info)); + List$deserialize(in, &path.components, pointers, List$info(&Text$info)); *(Path_t*)obj = path; } diff --git a/src/stdlib/paths.h b/src/stdlib/paths.h index 9be81bdf..31d676b7 100644 --- a/src/stdlib/paths.h +++ b/src/stdlib/paths.h @@ -32,22 +32,22 @@ OptionalInt64_t Path$modified(Path_t path, bool follow_symlinks); OptionalInt64_t Path$accessed(Path_t path, bool follow_symlinks); OptionalInt64_t Path$changed(Path_t path, bool follow_symlinks); void Path$write(Path_t path, Text_t text, int permissions); -void Path$write_bytes(Path_t path, Array_t bytes, int permissions); +void Path$write_bytes(Path_t path, List_t bytes, int permissions); void Path$append(Path_t path, Text_t text, int permissions); -void Path$append_bytes(Path_t path, Array_t bytes, int permissions); +void Path$append_bytes(Path_t path, List_t bytes, int permissions); OptionalText_t Path$read(Path_t path); -OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t limit); +OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t limit); void Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t group, bool follow_symlinks); OptionalText_t Path$owner(Path_t path, bool follow_symlinks); OptionalText_t Path$group(Path_t path, bool follow_symlinks); void Path$remove(Path_t path, bool ignore_missing); void Path$create_directory(Path_t path, int permissions); -Array_t Path$children(Path_t path, bool include_hidden); -Array_t Path$files(Path_t path, bool include_hidden); -Array_t Path$subdirectories(Path_t path, bool include_hidden); +List_t Path$children(Path_t path, bool include_hidden); +List_t Path$files(Path_t path, bool include_hidden); +List_t Path$subdirectories(Path_t path, bool include_hidden); Path_t Path$unique_directory(Path_t path); Path_t Path$write_unique(Path_t path, Text_t text); -Path_t Path$write_unique_bytes(Path_t path, Array_t bytes); +Path_t Path$write_unique_bytes(Path_t path, List_t bytes); Path_t Path$parent(Path_t path); Text_t Path$base_name(Path_t path); Text_t Path$extension(Path_t path, bool full); @@ -55,7 +55,7 @@ Path_t Path$with_component(Path_t path, Text_t component); Path_t Path$with_extension(Path_t path, Text_t extension, bool replace); Path_t Path$current_dir(void); Closure_t Path$by_line(Path_t path); -Array_t Path$glob(Path_t path); +List_t Path$glob(Path_t path); uint64_t Path$hash(const void *obj, const TypeInfo_t*); int32_t Path$compare(const void *a, const void *b, const TypeInfo_t *type); @@ -64,7 +64,7 @@ bool Path$equal_values(Path_t a, Path_t b); Text_t Path$as_text(const void *obj, bool color, const TypeInfo_t *type); bool Path$is_none(const void *obj, const TypeInfo_t *type); void Path$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type); -void Path$deserialize(FILE *in, void *obj, Array_t *pointers, const TypeInfo_t *type); +void Path$deserialize(FILE *in, void *obj, List_t *pointers, const TypeInfo_t *type); extern const TypeInfo_t Path$info; extern const TypeInfo_t PathType$info; diff --git a/src/stdlib/pointers.c b/src/stdlib/pointers.c index 76e882ec..b674ac6f 100644 --- a/src/stdlib/pointers.c +++ b/src/stdlib/pointers.c @@ -104,7 +104,7 @@ public void Pointer$serialize(const void *obj, FILE *out, Table_t *pointers, con _serialize(ptr, out, pointers, type->PointerInfo.pointed); } -public void Pointer$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type) +public void Pointer$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) { int64_t id = 0; Int64$deserialize(in, &id, pointers, &Int64$info); @@ -112,7 +112,7 @@ public void Pointer$deserialize(FILE *in, void *outval, Array_t *pointers, const if (id > pointers->length) { void *obj = GC_MALLOC((size_t)type->PointerInfo.pointed->size); - Array$insert(pointers, &obj, I(0), sizeof(void*)); + List$insert(pointers, &obj, I(0), sizeof(void*)); _deserialize(in, obj, pointers, type->PointerInfo.pointed); *(void**)outval = obj; } else { diff --git a/src/stdlib/pointers.h b/src/stdlib/pointers.h index 165a5184..b818452e 100644 --- a/src/stdlib/pointers.h +++ b/src/stdlib/pointers.h @@ -14,7 +14,7 @@ PUREFUNC int32_t Pointer$compare(const void *x, const void *y, const TypeInfo_t PUREFUNC bool Pointer$equal(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC bool Pointer$is_none(const void *x, const TypeInfo_t*); void Pointer$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type); -void Pointer$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type); +void Pointer$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type); #define Null(t) (t*)NULL #define POINTER_TYPE(_sigil, _pointed) (&(TypeInfo_t){\ diff --git a/src/stdlib/stdlib.c b/src/stdlib/stdlib.c index 8d3a8081..e8cfe06a 100644 --- a/src/stdlib/stdlib.c +++ b/src/stdlib/stdlib.c @@ -158,8 +158,8 @@ static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest) Text_t t = generic_as_text(NULL, false, info); print_err("Unsupported multi-argument struct type for argument parsing: ", t); - } else if (info->tag == ArrayInfo) { - print_err("Array arguments must be specified as `--flag ...` not `--flag=...`"); + } else if (info->tag == ListInfo) { + print_err("List arguments must be specified as `--flag ...` not `--flag=...`"); } else if (info->tag == TableInfo) { print_err("Table arguments must be specified as `--flag ...` not `--flag=...`"); } else { @@ -168,13 +168,13 @@ static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest) } } -static Array_t parse_array(const TypeInfo_t *item_info, int n, char *args[]) +static List_t parse_list(const TypeInfo_t *item_info, int n, char *args[]) { int64_t padded_size = item_info->size; if ((padded_size % item_info->align) > 0) padded_size = padded_size + item_info->align - (padded_size % item_info->align); - Array_t items = { + List_t items = { .stride=padded_size, .length=n, .data=GC_MALLOC((size_t)(padded_size*n)), @@ -199,7 +199,7 @@ static Table_t parse_table(const TypeInfo_t *table, int n, char *args[]) if ((padded_size % key->align) > 0) padded_size = padded_size + key->align - (padded_size % key->align); - Array_t entries = { + List_t entries = { .stride=padded_size, .length=n, .data=GC_MALLOC((size_t)(padded_size*n)), @@ -262,7 +262,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, char after_name = argv[i][2+strlen(spec[s].name)]; if (after_name == '\0') { // --foo val used_args[i] = true; - if (non_opt_type->tag == ArrayInfo) { + if (non_opt_type->tag == ListInfo) { int num_args = 0; while (i + 1 + num_args < argc) { if (argv[i+1+num_args][0] == '-') @@ -271,7 +271,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, num_args += 1; } populated_args[s] = true; - *(OptionalArray_t*)spec[s].dest = parse_array(non_opt_type->ArrayInfo.item, num_args, &argv[i+1]); + *(OptionalList_t*)spec[s].dest = parse_list(non_opt_type->ListInfo.item, num_args, &argv[i+1]); } else if (non_opt_type->tag == TableInfo) { int num_args = 0; while (i + 1 + num_args < argc) { @@ -322,7 +322,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, while (non_opt_type->tag == OptionalInfo) non_opt_type = non_opt_type->OptionalInfo.type; - if (non_opt_type->tag == ArrayInfo) { + if (non_opt_type->tag == ListInfo) { if (f[1]) print_err("No value provided for ", flag, "\n", usage); int num_args = 0; while (i + 1 + num_args < argc) { @@ -332,7 +332,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, num_args += 1; } populated_args[s] = true; - *(OptionalArray_t*)spec[s].dest = parse_array(non_opt_type->ArrayInfo.item, num_args, &argv[i+1]); + *(OptionalList_t*)spec[s].dest = parse_list(non_opt_type->ListInfo.item, num_args, &argv[i+1]); } else if (non_opt_type->tag == TableInfo) { int num_args = 0; while (i + 1 + num_args < argc) { @@ -398,7 +398,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, if (non_opt_type == &Bool$info) goto next_non_bool_flag; - if (non_opt_type->tag == ArrayInfo) { + if (non_opt_type->tag == ListInfo) { int num_args = 0; while (i + num_args < argc) { if (!ignore_dashes && argv[i+num_args][0] == '-') @@ -407,7 +407,7 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, num_args += 1; } populated_args[s] = true; - *(OptionalArray_t*)spec[s].dest = parse_array(non_opt_type->ArrayInfo.item, num_args, &argv[i]); + *(OptionalList_t*)spec[s].dest = parse_list(non_opt_type->ListInfo.item, num_args, &argv[i]); } else if (non_opt_type->tag == TableInfo) { int num_args = 0; while (i + num_args < argc) { @@ -428,8 +428,8 @@ public void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, for (int s = 0; s < spec_len; s++) { if (!populated_args[s] && spec[s].required) { - if (spec[s].type->tag == ArrayInfo) - *(OptionalArray_t*)spec[s].dest = (Array_t){}; + if (spec[s].type->tag == ListInfo) + *(OptionalList_t*)spec[s].dest = (List_t){}; else if (spec[s].type->tag == TableInfo) *(OptionalTable_t*)spec[s].dest = (Table_t){}; else diff --git a/src/stdlib/structs.c b/src/stdlib/structs.c index 53b0e0a4..9a14779e 100644 --- a/src/stdlib/structs.c +++ b/src/stdlib/structs.c @@ -3,7 +3,7 @@ #include <stdint.h> #include <string.h> -#include "arrays.h" +#include "lists.h" #include "bools.h" #include "functiontype.h" #include "metamethods.h" @@ -211,7 +211,7 @@ public void Struct$serialize(const void *obj, FILE *out, Table_t *pointers, cons } } -public void Struct$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type) +public void Struct$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) { ptrdiff_t byte_offset = 0; ptrdiff_t bit_offset = 0; diff --git a/src/stdlib/structs.h b/src/stdlib/structs.h index bab702cd..c9c6c40a 100644 --- a/src/stdlib/structs.h +++ b/src/stdlib/structs.h @@ -15,7 +15,7 @@ PUREFUNC bool PackedData$equal(const void *x, const void *y, const TypeInfo_t *t PUREFUNC Text_t Struct$as_text(const void *obj, bool colorize, const TypeInfo_t *type); PUREFUNC bool Struct$is_none(const void *obj, const TypeInfo_t *type); void Struct$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type); -void Struct$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type); +void Struct$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type); #define Struct$metamethods { \ .hash=Struct$hash, \ diff --git a/src/stdlib/tables.c b/src/stdlib/tables.c index d59bc113..3cb2e742 100644 --- a/src/stdlib/tables.c +++ b/src/stdlib/tables.c @@ -16,7 +16,7 @@ #include <string.h> #include <sys/param.h> -#include "arrays.h" +#include "lists.h" #include "c_strings.h" #include "datatypes.h" #include "memory.h" @@ -97,7 +97,7 @@ static INLINE void hshow(const Table_t *t) static void maybe_copy_on_write(Table_t *t, const TypeInfo_t *type) { if (t->entries.data_refcount != 0) - Array$compact(&t->entries, (int64_t)entry_size(type)); + List$compact(&t->entries, (int64_t)entry_size(type)); if (t->bucket_info && t->bucket_info->data_refcount != 0) { size_t size = sizeof(bucket_info_t) + sizeof(bucket_t[t->bucket_info->count]); @@ -273,7 +273,7 @@ public void *Table$reserve(Table_t *t, const void *key, const void *value, const memcpy(buf + value_offset(type), value, (size_t)value_size); else memset(buf + value_offset(type), 0, (size_t)value_size); - Array$insert(&t->entries, buf, I(0), (int64_t)entry_size(type)); + List$insert(&t->entries, buf, I(0), (int64_t)entry_size(type)); int64_t entry_index = t->entries.length-1; void *entry = GET_ENTRY(*t, entry_index); @@ -343,7 +343,7 @@ public void Table$remove(Table_t *t, const void *key, const TypeInfo_t *type) // instead of O(N) int64_t last_entry = t->entries.length-1; if (bucket->index != last_entry) { - hdebug("Removing key/value from the middle of the entries array\n"); + hdebug("Removing key/value from the middle of the entries list\n"); // Find the bucket that points to the last entry's index: uint64_t i = HASH_KEY(*t, GET_ENTRY(*t, last_entry)); @@ -353,7 +353,7 @@ public void Table$remove(Table_t *t, const void *key, const TypeInfo_t *type) // where the removed entry currently sits): t->bucket_info->buckets[i].index = bucket->index; - // Clobber the entry being removed (in the middle of the array) with + // Clobber the entry being removed (in the middle of the list) with // the last entry: memcpy(GET_ENTRY(*t, bucket->index), GET_ENTRY(*t, last_entry), entry_size(type)); } @@ -361,7 +361,7 @@ public void Table$remove(Table_t *t, const void *key, const TypeInfo_t *type) // Last entry is being removed, so clear it out to be safe: memset(GET_ENTRY(*t, last_entry), 0, entry_size(type)); - Array$remove_at(&t->entries, I(t->entries.length), I(1), (int64_t)entry_size(type)); + List$remove_at(&t->entries, I(t->entries.length), I(1), (int64_t)entry_size(type)); int64_t bucket_to_clear; if (prev) { // Middle (or end) of a chain @@ -399,7 +399,7 @@ public void Table$clear(Table_t *t) public Table_t Table$sorted(Table_t t, const TypeInfo_t *type) { Closure_t cmp = (Closure_t){.fn=generic_compare, .userdata=(void*)type->TableInfo.key}; - Array_t entries = Array$sorted(t.entries, cmp, (int64_t)entry_size(type)); + List_t entries = List$sorted(t.entries, cmp, (int64_t)entry_size(type)); return Table$from_entries(entries, type); } @@ -445,10 +445,10 @@ PUREFUNC public int32_t Table$compare(const void *vx, const void *vy, const Type // Table comparison rules: // - If two tables have different keys, then compare as if comparing a - // sorted array of the keys of the two tables: + // sorted list of the keys of the two tables: // `x.keys.sorted() <> y.keys.sorted()` - // - Otherwise, compare as if comparing arrays of values for the sorted key - // arrays: + // - Otherwise, compare as if comparing lists of values for the sorted key + // lists: // `[x[k] for k in x.keys.sorted()] <> [y[k] for k in y.keys.sorted()]` // // We can do this in _linear_ time if we find the smallest `k` such that @@ -612,7 +612,7 @@ public Text_t Table$as_text(const void *obj, bool colorize, const TypeInfo_t *ty return text; } -public Table_t Table$from_entries(Array_t entries, const TypeInfo_t *type) +public Table_t Table$from_entries(List_t entries, const TypeInfo_t *type) { assert(type->tag == TableInfo); if (entries.length == 0) @@ -776,7 +776,7 @@ public void Table$serialize(const void *obj, FILE *out, Table_t *pointers, const #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstack-protector" #endif -public void Table$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type) +public void Table$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) { int64_t len; Int64$deserialize(in, &len, pointers, &Int$info); diff --git a/src/stdlib/tables.h b/src/stdlib/tables.h index 979da5e7..35673eaf 100644 --- a/src/stdlib/tables.h +++ b/src/stdlib/tables.h @@ -6,14 +6,14 @@ #include <stdbool.h> #include <string.h> -#include "arrays.h" +#include "lists.h" #include "datatypes.h" #include "types.h" #include "util.h" #define Table(key_t, val_t, key_info, value_info, fb, N, ...) ({ \ struct { key_t k; val_t v; } ents[N] = {__VA_ARGS__}; \ - Table_t table = Table$from_entries((Array_t){ \ + Table_t table = Table$from_entries((List_t){ \ .data=memcpy(GC_MALLOC(sizeof(ents)), ents, sizeof(ents)), \ .length=sizeof(ents)/sizeof(ents[0]), \ .stride=(void*)&ents[1] - (void*)&ents[0], \ @@ -22,14 +22,14 @@ table; }) #define Set(item_t, item_info, N, ...) ({ \ item_t ents[N] = {__VA_ARGS__}; \ - Table_t set = Table$from_entries((Array_t){ \ + Table_t set = Table$from_entries((List_t){ \ .data=memcpy(GC_MALLOC(sizeof(ents)), ents, sizeof(ents)), \ .length=sizeof(ents)/sizeof(ents[0]), \ .stride=(void*)&ents[1] - (void*)&ents[0], \ }, Set$info(item_info)); \ set; }) -Table_t Table$from_entries(Array_t entries, const TypeInfo_t *type); +Table_t Table$from_entries(List_t entries, const TypeInfo_t *type); void *Table$get(Table_t t, const void *key, const TypeInfo_t *type); #define Table$get_optional(table_expr, key_t, val_t, key_expr, nonnull_var, nonnull_expr, null_expr, info_expr) ({ \ const Table_t t = table_expr; const key_t k = key_expr; \ @@ -71,7 +71,7 @@ PUREFUNC bool Table$is_superset_of(Table_t a, Table_t b, bool strict, const Type void Table$clear(Table_t *t); Table_t Table$sorted(Table_t t, const TypeInfo_t *type); void Table$mark_copy_on_write(Table_t *t); -#define TABLE_INCREF(t) ({ ARRAY_INCREF((t).entries); if ((t).bucket_info) (t).bucket_info->data_refcount += ((t).bucket_info->data_refcount < TABLE_MAX_DATA_REFCOUNT); }) +#define TABLE_INCREF(t) ({ LIST_INCREF((t).entries); if ((t).bucket_info) (t).bucket_info->data_refcount += ((t).bucket_info->data_refcount < TABLE_MAX_DATA_REFCOUNT); }) #define TABLE_COPY(t) ({ TABLE_INCREF(t); t; }) PUREFUNC int32_t Table$compare(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC bool Table$equal(const void *x, const void *y, const TypeInfo_t *type); @@ -86,7 +86,7 @@ void Table$str_set(Table_t *t, const char *key, const void *value); void *Table$str_reserve(Table_t *t, const char *key, const void *value); void Table$str_remove(Table_t *t, const char *key); void Table$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type); -void Table$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type); +void Table$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type); #define Table$length(t) ((t).entries.length) diff --git a/src/stdlib/text.c b/src/stdlib/text.c index b3e9cebb..3346bf4b 100644 --- a/src/stdlib/text.c +++ b/src/stdlib/text.c @@ -27,7 +27,7 @@ // is basically what Zalgo text is). // // There are a lot of benefits to storing unicode text with one grapheme -// cluster per index in a densely packed array instead of storing the text as +// cluster per index in a densely packed list instead of storing the text as // variable-width UTF8-encoded bytes. It lets us have one canonical length for // the text that can be precomputed and is meaningful to users. It lets us // quickly get the Nth "letter" in the text. Substring slicing is fast. @@ -38,7 +38,7 @@ // Graphemes, aka NFG). A synthetic grapheme is a negative 32-bit signed // integer that represents a multi-codepoint grapheme cluster that has been // encountered during the program's runtime. These clusters are stored in a -// lookup array and hash map so that we can rapidly convert between the +// lookup list and hash map so that we can rapidly convert between the // synthetic grapheme integer ID and the unicode codepoints associated with it. // Essentially, it's like we create a supplement to the unicode standard with // things that would be nice if they had their own codepoint so things worked @@ -49,7 +49,7 @@ // WITH ACUTE This would be stored as: (int32_t[]){0x48, 0xE9} Example 2: // U+0048, U+0065, U+0309 AKA: LATIN CAPITAL LETTER H, LATIN SMALL LETTER E, // COMBINING VERTICAL LINE BELOW This would be stored as: (int32_t[]){0x48, -2} -// Where -2 is used as a lookup in an array that holds the actual unicode +// Where -2 is used as a lookup in a list that holds the actual unicode // codepoints: (ucs4_t[]){0x65, 0x0309} #include <assert.h> @@ -68,7 +68,7 @@ #include <unistring/version.h> #include <uniwidth.h> -#include "arrays.h" +#include "lists.h" #include "integers.h" #include "tables.h" #include "text.h" @@ -86,7 +86,7 @@ typedef struct { // Synthetic grapheme clusters (clusters of more than one codepoint): static Table_t grapheme_ids_by_codepoints = {}; // ucs4_t* length-prefixed codepoints -> int32_t ID -// This will hold a dynamically growing array of synthetic graphemes: +// This will hold a dynamically growing list of synthetic graphemes: static synthetic_grapheme_t *synthetic_graphemes = NULL; static int32_t synthetic_grapheme_capacity = 0; static int32_t num_synthetic_graphemes = 0; @@ -733,7 +733,7 @@ Text_t text_from_u32(ucs4_t *codepoints, int64_t num_codepoints, bool normalize) // Intentionally overallocate here: allocate assuming each codepoint is a // grapheme cluster. If that's not true, we'll have extra space at the end - // of the array, but the length will still be calculated correctly. + // of the list, but the length will still be calculated correctly. int32_t *graphemes = GC_MALLOC_ATOMIC(sizeof(int32_t[num_codepoints])); struct Text_s ret = { .tag=TEXT_GRAPHEMES, @@ -1067,10 +1067,10 @@ public Text_t Text$translate(Text_t text, Table_t translations) TextIter_t text_state = NEW_TEXT_ITER_STATE(text); Text_t result = EMPTY_TEXT; int64_t span_start = 0; - Array_t replacement_array = translations.entries; + List_t replacement_list = translations.entries; for (int64_t i = 0; i < text.length; ) { - for (int64_t r = 0; r < replacement_array.length; r++) { - struct { Text_t target, replacement; } *entry = replacement_array.data + r*replacement_array.stride; + for (int64_t r = 0; r < replacement_list.length; r++) { + struct { Text_t target, replacement; } *entry = replacement_list.data + r*replacement_list.stride; TextIter_t target_state = NEW_TEXT_ITER_STATE(entry->target); if (_matches(&text_state, &target_state, i)) { if (i > span_start) @@ -1122,36 +1122,36 @@ public bool Text$has(Text_t text, Text_t target) return false; } -public Array_t Text$split(Text_t text, Text_t delimiters) +public List_t Text$split(Text_t text, Text_t delimiters) { if (delimiters.length == 0) return Text$clusters(text); TextIter_t text_state = NEW_TEXT_ITER_STATE(text), delim_state = NEW_TEXT_ITER_STATE(delimiters); - Array_t splits = {}; + List_t splits = {}; for (int64_t i = 0; i < text.length; ) { int64_t span_len = 0; while (i + span_len < text.length && !_matches(&text_state, &delim_state, i + span_len)) { span_len += 1; } Text_t slice = Text$slice(text, I(i+1), I(i+span_len)); - Array$insert(&splits, &slice, I(0), sizeof(slice)); + List$insert(&splits, &slice, I(0), sizeof(slice)); i += span_len + delimiters.length; if (i == text.length) { Text_t empty = Text(""); - Array$insert(&splits, &empty, I(0), sizeof(empty)); + List$insert(&splits, &empty, I(0), sizeof(empty)); } } return splits; } -public Array_t Text$split_any(Text_t text, Text_t delimiters) +public List_t Text$split_any(Text_t text, Text_t delimiters) { if (delimiters.length == 0) - return Array(text); + return List(text); TextIter_t text_state = NEW_TEXT_ITER_STATE(text), delim_state = NEW_TEXT_ITER_STATE(delimiters); - Array_t splits = {}; + List_t splits = {}; for (int64_t i = 0; i < text.length; ) { int64_t span_len = 0; while (i + span_len < text.length && !_has_grapheme(&delim_state, Text$get_grapheme_fast(&text_state, i + span_len))) { @@ -1159,14 +1159,14 @@ public Array_t Text$split_any(Text_t text, Text_t delimiters) } bool trailing_delim = i + span_len < text.length; Text_t slice = Text$slice(text, I(i+1), I(i+span_len)); - Array$insert(&splits, &slice, I(0), sizeof(slice)); + List$insert(&splits, &slice, I(0), sizeof(slice)); i += span_len + 1; while (i < text.length && _has_grapheme(&delim_state, Text$get_grapheme_fast(&text_state, i))) { i += 1; } if (i >= text.length && trailing_delim) { Text_t empty = Text(""); - Array$insert(&splits, &empty, I(0), sizeof(empty)); + List$insert(&splits, &empty, I(0), sizeof(empty)); } } return splits; @@ -1303,7 +1303,7 @@ PUREFUNC public bool Text$equal_ignoring_case(Text_t a, Text_t b, Text_t languag public Text_t Text$upper(Text_t text, Text_t language) { if (text.length == 0) return text; - Array_t codepoints = Text$utf32_codepoints(text); + List_t codepoints = Text$utf32_codepoints(text); const char *uc_language = Text$as_c_string(language); ucs4_t buf[128]; size_t out_len = sizeof(buf)/sizeof(buf[0]); @@ -1316,7 +1316,7 @@ public Text_t Text$upper(Text_t text, Text_t language) public Text_t Text$lower(Text_t text, Text_t language) { if (text.length == 0) return text; - Array_t codepoints = Text$utf32_codepoints(text); + List_t codepoints = Text$utf32_codepoints(text); const char *uc_language = Text$as_c_string(language); ucs4_t buf[128]; size_t out_len = sizeof(buf)/sizeof(buf[0]); @@ -1329,7 +1329,7 @@ public Text_t Text$lower(Text_t text, Text_t language) public Text_t Text$title(Text_t text, Text_t language) { if (text.length == 0) return text; - Array_t codepoints = Text$utf32_codepoints(text); + List_t codepoints = Text$utf32_codepoints(text); const char *uc_language = Text$as_c_string(language); ucs4_t buf[128]; size_t out_len = sizeof(buf)/sizeof(buf[0]); @@ -1451,7 +1451,7 @@ public Text_t Text$as_text(const void *vtext, bool colorize, const TypeInfo_t *i return as_text; } -public Text_t Text$join(Text_t glue, Array_t pieces) +public Text_t Text$join(Text_t glue, List_t pieces) { if (pieces.length == 0) return EMPTY_TEXT; @@ -1478,38 +1478,38 @@ public Text_t Text$format(const char *fmt, ...) return ret; } -public Array_t Text$clusters(Text_t text) +public List_t Text$clusters(Text_t text) { - Array_t clusters = {}; + List_t clusters = {}; for (int64_t i = 1; i <= text.length; i++) { Text_t cluster = Text$slice(text, I(i), I(i)); - Array$insert(&clusters, &cluster, I_small(0), sizeof(Text_t)); + List$insert(&clusters, &cluster, I_small(0), sizeof(Text_t)); } return clusters; } -public Array_t Text$utf32_codepoints(Text_t text) +public List_t Text$utf32_codepoints(Text_t text) { - Array_t codepoints = {.atomic=1}; + List_t codepoints = {.atomic=1}; TextIter_t state = NEW_TEXT_ITER_STATE(text); for (int64_t i = 0; i < text.length; i++) { int32_t grapheme = Text$get_grapheme_fast(&state, i); if (grapheme < 0) { for (int64_t c = 0; c < NUM_GRAPHEME_CODEPOINTS(grapheme); c++) { ucs4_t subg = GRAPHEME_CODEPOINTS(grapheme)[c]; - Array$insert(&codepoints, &subg, I_small(0), sizeof(ucs4_t)); + List$insert(&codepoints, &subg, I_small(0), sizeof(ucs4_t)); } } else { - Array$insert(&codepoints, &grapheme, I_small(0), sizeof(ucs4_t)); + List$insert(&codepoints, &grapheme, I_small(0), sizeof(ucs4_t)); } } return codepoints; } -public Array_t Text$utf8_bytes(Text_t text) +public List_t Text$utf8_bytes(Text_t text) { const char *str = Text$as_c_string(text); - return (Array_t){.length=strlen(str), .stride=1, .atomic=1, .data=(void*)str}; + return (List_t){.length=strlen(str), .stride=1, .atomic=1, .data=(void*)str}; } static INLINE const char *codepoint_name(ucs4_t c) @@ -1523,9 +1523,9 @@ static INLINE const char *codepoint_name(ucs4_t c) return name; } -public Array_t Text$codepoint_names(Text_t text) +public List_t Text$codepoint_names(Text_t text) { - Array_t names = {}; + List_t names = {}; TextIter_t state = NEW_TEXT_ITER_STATE(text); for (int64_t i = 0; i < text.length; i++) { int32_t grapheme = Text$get_grapheme_fast(&state, i); @@ -1533,65 +1533,65 @@ public Array_t Text$codepoint_names(Text_t text) for (int64_t c = 0; c < NUM_GRAPHEME_CODEPOINTS(grapheme); c++) { const char *name = codepoint_name(GRAPHEME_CODEPOINTS(grapheme)[c]); Text_t name_text = Text$from_str(name); - Array$insert(&names, &name_text, I_small(0), sizeof(Text_t)); + List$insert(&names, &name_text, I_small(0), sizeof(Text_t)); } } else { const char *name = codepoint_name((ucs4_t)grapheme); Text_t name_text = Text$from_str(name); - Array$insert(&names, &name_text, I_small(0), sizeof(Text_t)); + List$insert(&names, &name_text, I_small(0), sizeof(Text_t)); } } return names; } -public Text_t Text$from_codepoints(Array_t codepoints) +public Text_t Text$from_codepoints(List_t codepoints) { if (codepoints.stride != sizeof(int32_t)) - Array$compact(&codepoints, sizeof(int32_t)); + List$compact(&codepoints, sizeof(int32_t)); return text_from_u32(codepoints.data, codepoints.length, true); } -public OptionalText_t Text$from_codepoint_names(Array_t codepoint_names) +public OptionalText_t Text$from_codepoint_names(List_t codepoint_names) { - Array_t codepoints = {}; + List_t codepoints = {}; for (int64_t i = 0; i < codepoint_names.length; i++) { Text_t *name = ((Text_t*)(codepoint_names.data + i*codepoint_names.stride)); const char *name_str = Text$as_c_string(*name); ucs4_t codepoint = unicode_name_character(name_str); if (codepoint == UNINAME_INVALID) return NONE_TEXT; - Array$insert(&codepoints, &codepoint, I_small(0), sizeof(ucs4_t)); + List$insert(&codepoints, &codepoint, I_small(0), sizeof(ucs4_t)); } return Text$from_codepoints(codepoints); } -public OptionalText_t Text$from_bytes(Array_t bytes) +public OptionalText_t Text$from_bytes(List_t bytes) { if (bytes.stride != sizeof(int8_t)) - Array$compact(&bytes, sizeof(int8_t)); + List$compact(&bytes, sizeof(int8_t)); return Text$from_strn(bytes.data, (size_t)bytes.length); } -public Array_t Text$lines(Text_t text) +public List_t Text$lines(Text_t text) { - Array_t lines = {}; + List_t lines = {}; TextIter_t state = NEW_TEXT_ITER_STATE(text); for (int64_t i = 0, line_start = 0; i < text.length; i++) { int32_t grapheme = Text$get_grapheme_fast(&state, i); if (grapheme == '\r' && Text$get_grapheme_fast(&state, i + 1) == '\n') { // CRLF Text_t line = Text$slice(text, I(line_start+1), I(i)); - Array$insert(&lines, &line, I_small(0), sizeof(Text_t)); + List$insert(&lines, &line, I_small(0), sizeof(Text_t)); i += 1; // skip one extra for CR line_start = i + 1; } else if (grapheme == '\n') { // newline Text_t line = Text$slice(text, I(line_start+1), I(i)); - Array$insert(&lines, &line, I_small(0), sizeof(Text_t)); + List$insert(&lines, &line, I_small(0), sizeof(Text_t)); line_start = i + 1; } else if (i == text.length-1 && line_start != i) { // last line Text_t line = Text$slice(text, I(line_start+1), I(i+1)); - Array$insert(&lines, &line, I_small(0), sizeof(Text_t)); + List$insert(&lines, &line, I_small(0), sizeof(Text_t)); } } return lines; @@ -1645,7 +1645,7 @@ public void Text$serialize(const void *obj, FILE *out, Table_t *pointers, const fwrite(str, sizeof(char), (size_t)len, out); } -public void Text$deserialize(FILE *in, void *out, Array_t *pointers, const TypeInfo_t *) +public void Text$deserialize(FILE *in, void *out, List_t *pointers, const TypeInfo_t *) { int64_t len = -1; Int64$deserialize(in, &len, pointers, &Int64$info); diff --git a/src/stdlib/text.h b/src/stdlib/text.h index 662c6e5f..604cea44 100644 --- a/src/stdlib/text.h +++ b/src/stdlib/text.h @@ -55,24 +55,24 @@ Text_t Text$without_suffix(Text_t text, Text_t suffix); Text_t Text$replace(Text_t text, Text_t target, Text_t replacement); Text_t Text$translate(Text_t text, Table_t translations); bool Text$has(Text_t text, Text_t target); -Array_t Text$split(Text_t text, Text_t delimiter); -Array_t Text$split_any(Text_t text, Text_t delimiters); +List_t Text$split(Text_t text, Text_t delimiter); +List_t Text$split_any(Text_t text, Text_t delimiters); Closure_t Text$by_split(Text_t text, Text_t delimiter); Closure_t Text$by_split_any(Text_t text, Text_t delimiters); Text_t Text$trim(Text_t text, Text_t to_trim, bool left, bool right); char *Text$as_c_string(Text_t text); __attribute__((format(printf, 1, 2))) public Text_t Text$format(const char *fmt, ...); -Array_t Text$clusters(Text_t text); -Array_t Text$utf32_codepoints(Text_t text); -Array_t Text$utf8_bytes(Text_t text); -Array_t Text$codepoint_names(Text_t text); -Text_t Text$from_codepoints(Array_t codepoints); -OptionalText_t Text$from_codepoint_names(Array_t codepoint_names); -OptionalText_t Text$from_bytes(Array_t bytes); -Array_t Text$lines(Text_t text); +List_t Text$clusters(Text_t text); +List_t Text$utf32_codepoints(Text_t text); +List_t Text$utf8_bytes(Text_t text); +List_t Text$codepoint_names(Text_t text); +Text_t Text$from_codepoints(List_t codepoints); +OptionalText_t Text$from_codepoint_names(List_t codepoint_names); +OptionalText_t Text$from_bytes(List_t bytes); +List_t Text$lines(Text_t text); Closure_t Text$by_line(Text_t text); -Text_t Text$join(Text_t glue, Array_t pieces); +Text_t Text$join(Text_t glue, List_t pieces); Text_t Text$repeat(Text_t text, Int_t count); Int_t Text$width(Text_t text, Text_t language); Text_t Text$left_pad(Text_t text, Int_t width, Text_t padding, Text_t language); @@ -81,7 +81,7 @@ Text_t Text$middle_pad(Text_t text, Int_t width, Text_t padding, Text_t language int32_t Text$get_grapheme_fast(TextIter_t *state, int64_t index); uint32_t Text$get_main_grapheme_fast(TextIter_t *state, int64_t index); void Text$serialize(const void *obj, FILE *out, Table_t *, const TypeInfo_t *); -void Text$deserialize(FILE *in, void *out, Array_t *, const TypeInfo_t *); +void Text$deserialize(FILE *in, void *out, List_t *, const TypeInfo_t *); MACROLIKE int32_t Text$get_grapheme(Text_t text, int64_t index) { diff --git a/src/stdlib/tomo.h b/src/stdlib/tomo.h index b5642ec5..2d8d9908 100644 --- a/src/stdlib/tomo.h +++ b/src/stdlib/tomo.h @@ -7,7 +7,7 @@ #include <stdint.h> #include <sys/param.h> -#include "arrays.h" +#include "lists.h" #include "bools.h" #include "bytes.h" #include "c_strings.h" diff --git a/src/stdlib/types.c b/src/stdlib/types.c index 8ced9051..0f51d6ea 100644 --- a/src/stdlib/types.c +++ b/src/stdlib/types.c @@ -6,7 +6,7 @@ #include <sys/param.h> #include "util.h" -#include "arrays.h" +#include "lists.h" #include "pointers.h" #include "tables.h" #include "text.h" diff --git a/src/stdlib/types.h b/src/stdlib/types.h index 786e92d4..ddece7c9 100644 --- a/src/stdlib/types.h +++ b/src/stdlib/types.h @@ -17,7 +17,7 @@ typedef struct { Text_t (*as_text)(const void*, bool, const TypeInfo_t*); bool (*is_none)(const void*, const TypeInfo_t*); void (*serialize)(const void*, FILE*, Table_t*, const TypeInfo_t*); - void (*deserialize)(FILE*, void*, Array_t*, const TypeInfo_t*); + void (*deserialize)(FILE*, void*, List_t*, const TypeInfo_t*); } metamethods_t; typedef struct { @@ -29,7 +29,7 @@ struct TypeInfo_s { int64_t size, align; metamethods_t metamethods; struct { // Anonymous tagged union for convenience - enum { OpaqueInfo, StructInfo, EnumInfo, PointerInfo, TextInfo, ArrayInfo, TableInfo, FunctionInfo, + enum { OpaqueInfo, StructInfo, EnumInfo, PointerInfo, TextInfo, ListInfo, TableInfo, FunctionInfo, OptionalInfo, TypeInfoInfo } tag; union { struct {} OpaqueInfo; @@ -42,7 +42,7 @@ struct TypeInfo_s { } TextInfo; struct { const TypeInfo_t *item; - } ArrayInfo; + } ListInfo; struct { const TypeInfo_t *key, *value; } TableInfo; @@ -15,7 +15,7 @@ #include "cordhelpers.h" #include "parse.h" #include "repl.h" -#include "stdlib/arrays.h" +#include "stdlib/lists.h" #include "stdlib/bools.h" #include "stdlib/bytes.h" #include "stdlib/datatypes.h" @@ -28,14 +28,14 @@ #include "types.h" #define run_cmd(...) ({ const char *_cmd = String(__VA_ARGS__); if (verbose) print("\033[34;1m", _cmd, "\033[m"); popen(_cmd, "w"); }) -#define array_text(arr) Text$join(Text(" "), arr) +#define list_text(list) Text$join(Text(" "), list) #ifdef __linux__ // Only on Linux is /proc/self/exe available static struct stat compiler_stat; #endif -static const char *paths_str(Array_t paths) { +static const char *paths_str(List_t paths) { Text_t result = EMPTY_TEXT; for (int64_t i = 0; i < paths.length; i++) { if (i > 0) result = Texts(result, Text(" ")); @@ -44,10 +44,10 @@ static const char *paths_str(Array_t paths) { return Text$as_c_string(result); } -static OptionalArray_t files = NONE_ARRAY, - args = NONE_ARRAY, - uninstall = NONE_ARRAY, - libraries = NONE_ARRAY; +static OptionalList_t files = NONE_LIST, + args = NONE_LIST, + uninstall = NONE_LIST, + libraries = NONE_LIST; static OptionalBool_t verbose = false, quiet = false, stop_at_transpile = false, @@ -82,11 +82,11 @@ static const char *SHARED_SUFFIX = static void transpile_header(env_t *base_env, Path_t path); static void transpile_code(env_t *base_env, Path_t path); static void compile_object_file(Path_t path); -static Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, Array_t object_files, Array_t extra_ldlibs); +static Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t object_files, List_t extra_ldlibs); static void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_link); static Text_t escape_lib_name(Text_t lib_name); static void build_library(Text_t lib_dir_name); -static void compile_files(env_t *env, Array_t files, Array_t *object_files, Array_t *ldlibs); +static void compile_files(env_t *env, List_t files, List_t *object_files, List_t *ldlibs); static bool is_stale(Path_t path, Path_t relative_to); static Path_t build_file(Path_t path, const char *extension); static void wait_for_child_success(pid_t child); @@ -148,8 +148,8 @@ int main(int argc, char *argv[]) Text_t help = Texts(Text("\x1b[1mtomo\x1b[m: a compiler for the Tomo programming language"), Text("\n\n"), usage); tomo_parse_args( argc, argv, usage, help, - {"files", true, Array$info(&Path$info), &files}, - {"args", true, Array$info(&Text$info), &args}, + {"files", true, List$info(&Path$info), &files}, + {"args", true, List$info(&Text$info), &args}, {"verbose", false, &Bool$info, &verbose}, {"v", false, &Bool$info, &verbose}, {"quiet", false, &Bool$info, &quiet}, @@ -160,10 +160,10 @@ int main(int argc, char *argv[]) {"c", false, &Bool$info, &stop_at_obj_compilation}, {"compile-exe", false, &Bool$info, &compile_exe}, {"e", false, &Bool$info, &compile_exe}, - {"uninstall", false, Array$info(&Text$info), &uninstall}, - {"u", false, Array$info(&Text$info), &uninstall}, - {"library", false, Array$info(&Path$info), &libraries}, - {"L", false, Array$info(&Path$info), &libraries}, + {"uninstall", false, List$info(&Text$info), &uninstall}, + {"u", false, List$info(&Text$info), &uninstall}, + {"library", false, List$info(&Path$info), &libraries}, + {"L", false, List$info(&Path$info), &libraries}, {"show-codegen", false, &Text$info, &show_codegen}, {"C", false, &Text$info, &show_codegen}, {"repl", false, &Bool$info, &run_repl}, @@ -252,7 +252,7 @@ int main(int argc, char *argv[]) pid_t child = fork(); if (child == 0) { env_t *env = global_env(); - Array_t object_files = {}, + List_t object_files = {}, extra_ldlibs = {}; compile_files(env, files, &object_files, &extra_ldlibs); compile_executable(env, path, exe_path, object_files, extra_ldlibs); @@ -407,9 +407,9 @@ static void _compile_file_header_for_library(env_t *env, Path_t header_path, Pat void build_library(Text_t lib_dir_name) { - Array_t tm_files = Path$glob(Path("./[!._0-9]*.tm")); + List_t tm_files = Path$glob(Path("./[!._0-9]*.tm")); env_t *env = fresh_scope(global_env()); - Array_t object_files = {}, + List_t object_files = {}, extra_ldlibs = {}; // Resolve all files to absolute paths: @@ -455,7 +455,7 @@ void build_library(Text_t lib_dir_name) errx(WEXITSTATUS(status), "Failed to create symbol rename table with `nm` and `sed`"); } - prog = run_cmd(cc, " -O", optimization, " ", cflags, " ", ldflags, " ", ldlibs, " ", array_text(extra_ldlibs), + prog = run_cmd(cc, " -O", optimization, " ", cflags, " ", ldflags, " ", ldlibs, " ", list_text(extra_ldlibs), #ifdef __APPLE__ " -Wl,-install_name,@rpath/'lib", lib_dir_name, SHARED_SUFFIX, "'" #else @@ -501,7 +501,7 @@ void build_library(Text_t lib_dir_name) } } -void compile_files(env_t *env, Array_t to_compile, Array_t *object_files, Array_t *extra_ldlibs) +void compile_files(env_t *env, List_t to_compile, List_t *object_files, List_t *extra_ldlibs) { Table_t to_link = {}; Table_t dependency_files = {}; @@ -562,13 +562,13 @@ void compile_files(env_t *env, Array_t to_compile, Array_t *object_files, Array_ for (int64_t i = 0; i < dependency_files.entries.length; i++) { Path_t path = *(Path_t*)(dependency_files.entries.data + i*dependency_files.entries.stride); path = build_file(path, ".o"); - Array$insert(object_files, &path, I(0), sizeof(Path_t)); + List$insert(object_files, &path, I(0), sizeof(Path_t)); } } if (extra_ldlibs) { for (int64_t i = 0; i < to_link.entries.length; i++) { Text_t lib = *(Text_t*)(to_link.entries.data + i*to_link.entries.stride); - Array$insert(extra_ldlibs, &lib, I(0), sizeof(Text_t)); + List$insert(extra_ldlibs, &lib, I(0), sizeof(Text_t)); } } } @@ -615,7 +615,7 @@ void build_file_dependency_graph(Path_t path, Table_t *to_compile, Table_t *to_l Text_t lib = Text$format("'%s/.local/share/tomo/installed/%s/lib%s%s'", getenv("HOME"), use->path, use->path, SHARED_SUFFIX); Table$set(to_link, &lib, ((Bool_t[1]){1}), Table$info(&Text$info, &Bool$info)); - Array_t children = Path$glob(Path$from_str(String(getenv("HOME"), "/.local/share/tomo/installed/", use->path, "/*.tm"))); + List_t children = Path$glob(Path$from_str(String(getenv("HOME"), "/.local/share/tomo/installed/", use->path, "/*.tm"))); for (int64_t i = 0; i < children.length; i++) { Path_t *child = (Path_t*)(children.data + i*children.stride); Table_t discarded = {.fallback=to_compile}; @@ -745,7 +745,7 @@ void compile_object_file(Path_t path) print("Compiled object:\t", obj_file); } -Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, Array_t object_files, Array_t extra_ldlibs) +Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t object_files, List_t extra_ldlibs) { ast_t *ast = parse_file(Path$as_c_string(path), NULL); if (!ast) @@ -755,7 +755,7 @@ Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, Array_t if (!main_binding || main_binding->type->tag != FunctionType) print_err("No main() function has been defined for ", path, ", so it can't be run!"); - FILE *runner = run_cmd(cc, " ", cflags, " -O", optimization, " ", ldflags, " ", ldlibs, " ", array_text(extra_ldlibs), " ", + FILE *runner = run_cmd(cc, " ", cflags, " -O", optimization, " ", ldflags, " ", ldlibs, " ", list_text(extra_ldlibs), " ", paths_str(object_files), " -x c - -o ", exe_path); CORD program = CORD_all( "extern int ", main_binding->code, "$parse_and_run(int argc, char *argv[]);\n" diff --git a/src/typecheck.c b/src/typecheck.c index a869f73e..ce8b4276 100644 --- a/src/typecheck.c +++ b/src/typecheck.c @@ -52,26 +52,26 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast) code_err(ast, "Void pointers are not supported. You probably meant 'Memory' instead of 'Void'"); return Type(PointerType, .pointed=pointed_t, .is_stack=ptr->is_stack); } - case ArrayTypeAST: { - type_ast_t *item_type = Match(ast, ArrayTypeAST)->item; + case ListTypeAST: { + type_ast_t *item_type = Match(ast, ListTypeAST)->item; type_t *item_t = parse_type_ast(env, item_type); if (!item_t) code_err(item_type, "I can't figure out what this type is."); if (has_stack_memory(item_t)) - code_err(item_type, "Arrays can't have stack references because the array may outlive the stack frame."); - if (type_size(item_t) > ARRAY_MAX_STRIDE) - code_err(ast, "This array holds items that take up ", (uint64_t)type_size(item_t), - " bytes, but the maximum supported size is ", ARRAY_MAX_STRIDE, " bytes. Consider using an array of pointers instead."); - return Type(ArrayType, .item_type=item_t); + code_err(item_type, "Lists can't have stack references because the list may outlive the stack frame."); + if (type_size(item_t) > LIST_MAX_STRIDE) + code_err(ast, "This list holds items that take up ", (uint64_t)type_size(item_t), + " bytes, but the maximum supported size is ", LIST_MAX_STRIDE, " bytes. Consider using a list of pointers instead."); + return Type(ListType, .item_type=item_t); } case SetTypeAST: { type_ast_t *item_type = Match(ast, SetTypeAST)->item; type_t *item_t = parse_type_ast(env, item_type); if (!item_t) code_err(item_type, "I can't figure out what this type is."); if (has_stack_memory(item_t)) - code_err(item_type, "Sets can't have stack references because the array may outlive the stack frame."); - if (type_size(item_t) > ARRAY_MAX_STRIDE) + code_err(item_type, "Sets can't have stack references because the list may outlive the stack frame."); + if (type_size(item_t) > LIST_MAX_STRIDE) code_err(ast, "This set holds items that take up ", (uint64_t)type_size(item_t), - " bytes, but the maximum supported size is ", ARRAY_MAX_STRIDE, " bytes. Consider using an set of pointers instead."); + " bytes, but the maximum supported size is ", LIST_MAX_STRIDE, " bytes. Consider using an set of pointers instead."); return Type(SetType, .item_type=item_t); } case TableTypeAST: { @@ -80,12 +80,12 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast) type_t *key_type = parse_type_ast(env, key_type_ast); if (!key_type) code_err(key_type_ast, "I can't figure out what type this is."); if (has_stack_memory(key_type)) - code_err(key_type_ast, "Tables can't have stack references because the array may outlive the stack frame."); + code_err(key_type_ast, "Tables can't have stack references because the list may outlive the stack frame."); type_t *val_type = parse_type_ast(env, table_type->value); if (!val_type) code_err(table_type->value, "I can't figure out what type this is."); if (has_stack_memory(val_type)) - code_err(table_type->value, "Tables can't have stack references because the array may outlive the stack frame."); + code_err(table_type->value, "Tables can't have stack references because the list may outlive the stack frame."); else if (val_type->tag == OptionalType) code_err(ast, "Tables with optional-typed values are not currently supported"); @@ -273,7 +273,7 @@ void prebind_statement(env_t *env, ast_t *statement) extended->libname = env->libname; for (ast_list_t *stmt = extend->body ? Match(extend->body, Block)->statements : NULL; stmt; stmt = stmt->next) prebind_statement(extended, stmt->ast); - Array_t new_bindings = extended->locals->entries; + List_t new_bindings = extended->locals->entries; for (int64_t i = 0; i < new_bindings.length; i++) { struct { const char *name; binding_t *binding; } *entry = new_bindings.data + i*new_bindings.stride; binding_t *clobbered = Table$str_get(*ns_env->locals, entry->name); @@ -334,7 +334,7 @@ void bind_statement(env_t *env, ast_t *statement) get_line_number(statement->file, statement->start)); binding_t binding = {.type=type, .code=code}; env_t *type_ns = get_namespace_by_type(env, ret_t); - Array$insert(&type_ns->namespace->constructors, &binding, I(0), sizeof(binding)); + List$insert(&type_ns->namespace->constructors, &binding, I(0), sizeof(binding)); break; } case StructDef: { @@ -461,7 +461,7 @@ void bind_statement(env_t *env, ast_t *statement) extended->libname = env->libname; for (ast_list_t *stmt = extend->body ? Match(extend->body, Block)->statements : NULL; stmt; stmt = stmt->next) bind_statement(extended, stmt->ast); - Array_t new_bindings = extended->locals->entries; + List_t new_bindings = extended->locals->entries; for (int64_t i = 0; i < new_bindings.length; i++) { struct { const char *name; binding_t *binding; } *entry = new_bindings.data + i*new_bindings.stride; binding_t *clobbered = Table$str_get(*ns_env->locals, entry->name); @@ -660,7 +660,7 @@ type_t *get_type(env_t *env, ast_t *ast) code_err(ast, "'&' stack references can only be used on the fields of pointers and local variables"); } case Index: - code_err(ast, "'&' stack references are not supported for array or table indexing"); + code_err(ast, "'&' stack references are not supported for list or table indexing"); default: return Type(PointerType, .pointed=get_type(env, value), .is_stack=true); } @@ -698,10 +698,10 @@ type_t *get_type(env_t *env, ast_t *ast) if (b) return b->type; code_err(ast, "I don't know what ", quoted(var->name), " refers to"); } - case Array: { - auto array = Match(ast, Array); + case List: { + auto list = Match(ast, List); type_t *item_type = NULL; - for (ast_list_t *item = array->items; item; item = item->next) { + for (ast_list_t *item = list->items; item; item = item->next) { ast_t *item_ast = item->ast; env_t *scope = env; while (item_ast->tag == Comprehension) { @@ -714,15 +714,15 @@ type_t *get_type(env_t *env, ast_t *ast) type_t *merged = item_type ? type_or_type(item_type, t2) : t2; if (!merged) code_err(item->ast, - "This array item has type ", type_to_str(t2), - ", which is different from earlier array items which have type ", type_to_str(item_type)); + "This list item has type ", type_to_str(t2), + ", which is different from earlier list items which have type ", type_to_str(item_type)); item_type = merged; } if (item_type && has_stack_memory(item_type)) - code_err(ast, "Arrays cannot hold stack references, because the array may outlive the stack frame the reference was created in."); + code_err(ast, "Lists cannot hold stack references, because the list may outlive the stack frame the reference was created in."); - return Type(ArrayType, .item_type=item_type); + return Type(ListType, .item_type=item_type); } case Set: { auto set = Match(ast, Set); @@ -800,7 +800,7 @@ type_t *get_type(env_t *env, ast_t *ast) auto e = Match(comp->expr, TableEntry); return Type(TableType, .key_type=get_type(scope, e->key), .value_type=get_type(scope, e->value), .env=env); } else { - return Type(ArrayType, .item_type=get_type(scope, comp->expr)); + return Type(ListType, .item_type=get_type(scope, comp->expr)); } } case FieldAccess: { @@ -833,11 +833,11 @@ type_t *get_type(env_t *env, ast_t *ast) return Match(indexed_t, PointerType)->pointed; type_t *value_t = value_type(indexed_t); - if (value_t->tag == ArrayType) { + if (value_t->tag == ListType) { if (!indexing->index) return indexed_t; type_t *index_t = get_type(env, indexing->index); if (index_t->tag == IntType || index_t->tag == BigIntType || index_t->tag == ByteType) - return Match(value_t, ArrayType)->item_type; + return Match(value_t, ListType)->item_type; code_err(indexing->index, "I only know how to index lists using integers, not ", type_to_str(index_t)); } else if (value_t->tag == TableType) { auto table_type = Match(value_t, TableType); @@ -878,7 +878,7 @@ type_t *get_type(env_t *env, ast_t *ast) auto call = Match(ast, MethodCall); if (streq(call->name, "serialized")) // Data serialization - return Type(ArrayType, Type(ByteType)); + return Type(ListType, Type(ByteType)); type_t *self_value_t = get_type(env, call->self); if (!self_value_t) code_err(call->self, "Couldn't get the type of this value"); @@ -890,8 +890,8 @@ type_t *get_type(env_t *env, ast_t *ast) } switch (self_value_t->tag) { - case ArrayType: { - type_t *item_type = Match(self_value_t, ArrayType)->item_type; + case ListType: { + type_t *item_type = Match(self_value_t, ListType)->item_type; if (streq(call->name, "binary_search")) return INT_TYPE; else if (streq(call->name, "by")) return self_value_t; else if (streq(call->name, "clear")) return Type(VoidType); @@ -918,7 +918,7 @@ type_t *get_type(env_t *env, ast_t *ast) else if (streq(call->name, "sorted")) return self_value_t; else if (streq(call->name, "to")) return self_value_t; else if (streq(call->name, "unique")) return Type(SetType, .item_type=item_type); - else code_err(ast, "There is no '", call->name, "' method for arrays"); + else code_err(ast, "There is no '", call->name, "' method for lists"); } case SetType: { if (streq(call->name, "add")) return Type(VoidType); @@ -1266,7 +1266,7 @@ type_t *get_type(env_t *env, ast_t *ast) binding_t *b = get_metamethod_binding(env, ast->tag, binop.lhs, binop.rhs, overall_t); if (b) return overall_t; - if (overall_t->tag == ArrayType || overall_t->tag == SetType || overall_t->tag == TextType) + if (overall_t->tag == ListType || overall_t->tag == SetType || overall_t->tag == TextType) return overall_t; code_err(ast, "I don't know how to do concatenation between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); @@ -1680,9 +1680,9 @@ PUREFUNC bool can_compile_to_type(env_t *env, ast_t *ast, type_t *needed) return true; needed = non_optional(needed); - if (needed->tag == ArrayType && ast->tag == Array) { - type_t *item_type = Match(needed, ArrayType)->item_type; - for (ast_list_t *item = Match(ast, Array)->items; item; item = item->next) { + if (needed->tag == ListType && ast->tag == List) { + type_t *item_type = Match(needed, ListType)->item_type; + for (ast_list_t *item = Match(ast, List)->items; item; item = item->next) { if (!can_compile_to_type(env, item->ast, item_type)) return false; } diff --git a/src/types.c b/src/types.c index 0433cc48..83c3e702 100644 --- a/src/types.c +++ b/src/types.c @@ -33,9 +33,9 @@ CORD type_to_cord(type_t *t) { case BigIntType: return "Int"; case IntType: return CORD_asprintf("Int%d", Match(t, IntType)->bits); case NumType: return Match(t, NumType)->bits == TYPE_NBITS32 ? "Num32" : "Num"; - case ArrayType: { - auto array = Match(t, ArrayType); - return CORD_asprintf("[%r]", type_to_cord(array->item_type)); + case ListType: { + auto list = Match(t, ListType); + return CORD_asprintf("[%r]", type_to_cord(list->item_type)); } case TableType: { auto table = Match(t, TableType); @@ -240,7 +240,7 @@ PUREFUNC precision_cmp_e compare_precision(type_t *a, type_t *b) PUREFUNC bool has_heap_memory(type_t *t) { switch (t->tag) { - case ArrayType: return true; + case ListType: return true; case TableType: return true; case SetType: return true; case PointerType: return true; @@ -367,7 +367,7 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed) } // Empty literals: - if (actual->tag == ArrayType && needed->tag == ArrayType && Match(actual, ArrayType)->item_type == NULL) + if (actual->tag == ListType && needed->tag == ListType && Match(actual, ListType)->item_type == NULL) return true; // [] -> [T] if (actual->tag == SetType && needed->tag == SetType && Match(actual, SetType)->item_type == NULL) return true; // || -> |T| @@ -410,9 +410,9 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed) && can_promote(actual_ret, needed_ret))); } - // Set -> Array promotion - if (needed->tag == ArrayType && actual->tag == SetType - && type_eq(Match(needed, ArrayType)->item_type, Match(actual, SetType)->item_type)) + // Set -> List promotion + if (needed->tag == ListType && actual->tag == SetType + && type_eq(Match(needed, ListType)->item_type, Match(actual, SetType)->item_type)) return true; return false; @@ -508,7 +508,7 @@ PUREFUNC size_t type_size(type_t *t) } case NumType: return Match(t, NumType)->bits == TYPE_NBITS64 ? sizeof(double) : sizeof(float); case TextType: return sizeof(Text_t); - case ArrayType: return sizeof(Array_t); + case ListType: return sizeof(List_t); case SetType: return sizeof(Table_t); case TableType: return sizeof(Table_t); case FunctionType: return sizeof(void*); @@ -599,7 +599,7 @@ PUREFUNC size_t type_align(type_t *t) case NumType: return Match(t, NumType)->bits == TYPE_NBITS64 ? __alignof__(double) : __alignof__(float); case TextType: return __alignof__(Text_t); case SetType: return __alignof__(Table_t); - case ArrayType: return __alignof__(Array_t); + case ListType: return __alignof__(List_t); case TableType: return __alignof__(Table_t); case FunctionType: return __alignof__(void*); case ClosureType: return __alignof__(struct {void *fn, *userdata;}); @@ -680,21 +680,21 @@ type_t *get_field_type(type_t *t, const char *field_name) if (streq(field_name, "length")) return INT_TYPE; else if (streq(field_name, "items")) - return Type(ArrayType, .item_type=Match(t, SetType)->item_type); + return Type(ListType, .item_type=Match(t, SetType)->item_type); return NULL; } case TableType: { if (streq(field_name, "length")) return INT_TYPE; else if (streq(field_name, "keys")) - return Type(ArrayType, Match(t, TableType)->key_type); + return Type(ListType, Match(t, TableType)->key_type); else if (streq(field_name, "values")) - return Type(ArrayType, Match(t, TableType)->value_type); + return Type(ListType, Match(t, TableType)->value_type); else if (streq(field_name, "fallback")) return Type(OptionalType, .type=t); return NULL; } - case ArrayType: { + case ListType: { if (streq(field_name, "length")) return INT_TYPE; return NULL; } @@ -707,7 +707,7 @@ PUREFUNC type_t *get_iterated_type(type_t *t) type_t *iter_value_t = value_type(t); switch (iter_value_t->tag) { case BigIntType: case IntType: return iter_value_t; break; - case ArrayType: return Match(iter_value_t, ArrayType)->item_type; break; + case ListType: return Match(iter_value_t, ListType)->item_type; break; case SetType: return Match(iter_value_t, SetType)->item_type; break; case TableType: return NULL; case FunctionType: case ClosureType: { @@ -728,7 +728,7 @@ CONSTFUNC bool is_incomplete_type(type_t *t) switch (t->tag) { case ReturnType: return is_incomplete_type(Match(t, ReturnType)->ret); case OptionalType: return is_incomplete_type(Match(t, OptionalType)->type); - case ArrayType: return is_incomplete_type(Match(t, ArrayType)->item_type); + case ListType: return is_incomplete_type(Match(t, ListType)->item_type); case SetType: return is_incomplete_type(Match(t, SetType)->item_type); case TableType: { auto table = Match(t, TableType); @@ -770,9 +770,9 @@ CONSTFUNC type_t *most_complete_type(type_t *t1, type_t *t2) type_t *opt = most_complete_type(Match(t1, OptionalType)->type, Match(t2, OptionalType)->type); return opt ? Type(OptionalType, opt) : NULL; } - case ArrayType: { - type_t *item = most_complete_type(Match(t1, ArrayType)->item_type, Match(t2, ArrayType)->item_type); - return item ? Type(ArrayType, item) : NULL; + case ListType: { + type_t *item = most_complete_type(Match(t1, ListType)->item_type, Match(t2, ListType)->item_type); + return item ? Type(ListType, item) : NULL; } case SetType: { type_t *item = most_complete_type(Match(t1, SetType)->item_type, Match(t2, SetType)->item_type); diff --git a/src/types.h b/src/types.h index 2d2bd2a3..33e54ff5 100644 --- a/src/types.h +++ b/src/types.h @@ -47,7 +47,7 @@ struct type_s { NumType, CStringType, TextType, - ArrayType, + ListType, SetType, TableType, FunctionType, @@ -81,7 +81,7 @@ struct type_s { } TextType; struct { type_t *item_type; - } ArrayType; + } ListType; struct { type_t *item_type; } SetType; diff --git a/test/arrays.tm b/test/arrays.tm index 8122106c..71e107ef 100644 --- a/test/arrays.tm +++ b/test/arrays.tm @@ -8,42 +8,42 @@ func main() = [] do - >> arr := [10, 20, 30] + >> list := [10, 20, 30] = [10, 20, 30] - >> arr[1] + >> list[1] = 10 - >> arr[-1] + >> list[-1] = 30 - >> arr.length + >> list.length = 3 sum := 0 - for x in arr + for x in list sum += x >> sum = 60 str := "" - for i,x in arr + for i,x in list str ++= "($i,$x)" >> str = "(1,10)(2,20)(3,30)" do - >> arr := [10, 20] ++ [30, 40] + >> list := [10, 20] ++ [30, 40] = [10, 20, 30, 40] - >> arr ++= [50, 60] - >> arr + >> list ++= [50, 60] + >> list = [10, 20, 30, 40, 50, 60] do - >> arr := [10, 20] - >> copy := arr - >> arr ++= [30] - >> arr + >> list := [10, 20] + >> copy := list + >> list ++= [30] + >> list = [10, 20, 30] >> copy = [10, 20] @@ -62,24 +62,24 @@ func main() = [2, 3, 2, 3, 4] do - >> arr := @[10, 20] - >> copy := arr[] - >> arr.insert(30) - >> arr + >> list := @[10, 20] + >> copy := list[] + >> list.insert(30) + >> list = @[10, 20, 30] >> copy = [10, 20] - >> arr[1] = 999 - >> arr + >> list[1] = 999 + >> list = @[999, 20, 30] do - >> arr := &[10, 20, 30] - >> reversed := arr.reversed() + >> list := &[10, 20, 30] + >> reversed := list.reversed() = [30, 20, 10] # Ensure the copy-on-write behavior triggers: - >> arr[1] = 999 + >> list[1] = 999 >> reversed = [30, 20, 10] @@ -148,7 +148,7 @@ func main() >> [i*10 for i in 10].by(2).by(-1) = [90, 70, 50, 30, 10] - # Test iterating over array.from() and array.to() + # Test iterating over list.from() and list.to() xs := ["A", "B", "C", "D"] for i,x in xs.to(-2) for y in xs.from(i+1) diff --git a/test/optionals.tm b/test/optionals.tm index 36f38625..50e700f1 100644 --- a/test/optionals.tm +++ b/test/optionals.tm @@ -25,7 +25,7 @@ func maybe_int64(should_i:Bool->Int64?) else return none -func maybe_array(should_i:Bool->[Int]?) +func maybe_list(should_i:Bool->[Int]?) if should_i return [10, 20, 30] else @@ -114,10 +114,10 @@ func main() else say("Falsey: $nope") do - say("Arrays:") - >> yep := maybe_array(yes) + say("Lists:") + >> yep := maybe_list(yes) = [10, 20, 30]? - >> nope := maybe_array(no) + >> nope := maybe_list(no) = none >> if yep >> yep |
