From b752d0f75f318ed348bbd8a2c495177565a95220 Mon Sep 17 00:00:00 2001 From: Maksym Hazevych Date: Sun, 17 Nov 2024 03:31:59 +0200 Subject: [PATCH 01/13] std: Add function to get last item of array --- src/std/array.ab | 5 +++++ src/tests/stdlib/array_last.ab | 6 ++++++ 2 files changed, 11 insertions(+) create mode 100644 src/tests/stdlib/array_last.ab diff --git a/src/std/array.ab b/src/std/array.ab index 2fcf2e6f..c5209c71 100644 --- a/src/std/array.ab +++ b/src/std/array.ab @@ -26,3 +26,8 @@ pub fun includes(array, value) { let result = array_first_index(array, value) return result >= 0 } + +/// Returns the last element in the array +pub fun last(array) { + return array[len(array) - 1] +} diff --git a/src/tests/stdlib/array_last.ab b/src/tests/stdlib/array_last.ab new file mode 100644 index 00000000..d00e2000 --- /dev/null +++ b/src/tests/stdlib/array_last.ab @@ -0,0 +1,6 @@ +import { last } from "std/array" + +main { + let array = ["Failed", "Not", "Succeeded"] + echo last(array) +} From eaf9317be6c2713abfe06c82ac7e9e0e6e93de72 Mon Sep 17 00:00:00 2001 From: Maksym Hazevych Date: Sun, 17 Nov 2024 03:44:24 +0200 Subject: [PATCH 02/13] std: Add function to remove element from array --- src/std/array.ab | 5 +++++ src/tests/stdlib/array_remove_at.ab | 9 +++++++++ 2 files changed, 14 insertions(+) create mode 100644 src/tests/stdlib/array_remove_at.ab diff --git a/src/std/array.ab b/src/std/array.ab index c5209c71..d368382e 100644 --- a/src/std/array.ab +++ b/src/std/array.ab @@ -31,3 +31,8 @@ pub fun includes(array, value) { pub fun last(array) { return array[len(array) - 1] } + +/// Removes an element at the index from the array +pub fun remove_at(ref array: [], index: Num): Null { + trust $ unset {nameof array}[{index}] $ +} diff --git a/src/tests/stdlib/array_remove_at.ab b/src/tests/stdlib/array_remove_at.ab new file mode 100644 index 00000000..4c32469d --- /dev/null +++ b/src/tests/stdlib/array_remove_at.ab @@ -0,0 +1,9 @@ +import { remove_at, array_first_index } from "std/array" + +main { + let array = ["Failed", "Removed", "Failed"] + remove_at(array, 1) + if len(array) == 2 and array_first_index(array, "Removed") == -1 { + echo "Succeeded" + } +} From 525cf23873dec5041349ff12a34858a2a8cadae4 Mon Sep 17 00:00:00 2001 From: Maksym Hazevych Date: Sun, 17 Nov 2024 03:52:23 +0200 Subject: [PATCH 03/13] std: Add function to extract element from array --- src/std/array.ab | 7 +++++++ src/tests/stdlib/array_extract_at.ab | 12 ++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/tests/stdlib/array_extract_at.ab diff --git a/src/std/array.ab b/src/std/array.ab index d368382e..b37e7853 100644 --- a/src/std/array.ab +++ b/src/std/array.ab @@ -36,3 +36,10 @@ pub fun last(array) { pub fun remove_at(ref array: [], index: Num): Null { trust $ unset {nameof array}[{index}] $ } + +/// Removes an element at the index from the array, and returns it +pub fun extract_at(ref array, index) { + let elem = array[index] + remove_at(array, index) + return elem +} diff --git a/src/tests/stdlib/array_extract_at.ab b/src/tests/stdlib/array_extract_at.ab new file mode 100644 index 00000000..606fe989 --- /dev/null +++ b/src/tests/stdlib/array_extract_at.ab @@ -0,0 +1,12 @@ +import { extract_at, array_first_index } from "std/array" + +main { + let array = ["Failed", "Extracted", "Failed"] + + if extract_at(array, 1) == "Extracted" + and len(array) == 2 + and array_first_index(array, "Extracted") == -1 + { + echo "Succeeded" + } +} From 061e40faee25bf0d938a0b7e09fce982b39e9c4c Mon Sep 17 00:00:00 2001 From: Maksym Hazevych Date: Sun, 17 Nov 2024 03:52:49 +0200 Subject: [PATCH 04/13] std: Add functions to pop and shift elements from array --- src/std/array.ab | 10 ++++++++++ src/tests/stdlib/array_pop.ab | 11 +++++++++++ src/tests/stdlib/array_shift.ab | 11 +++++++++++ 3 files changed, 32 insertions(+) create mode 100644 src/tests/stdlib/array_pop.ab create mode 100644 src/tests/stdlib/array_shift.ab diff --git a/src/std/array.ab b/src/std/array.ab index b37e7853..45e3a9e9 100644 --- a/src/std/array.ab +++ b/src/std/array.ab @@ -43,3 +43,13 @@ pub fun extract_at(ref array, index) { remove_at(array, index) return elem } + +/// Removes the last element from the array, and returns it +pub fun pop(ref array) { + return extract_at(array, len(array) - 1) +} + +/// Removes the first element from the array, and returns it +pub fun shift(ref array) { + return extract_at(array, 0) +} diff --git a/src/tests/stdlib/array_pop.ab b/src/tests/stdlib/array_pop.ab new file mode 100644 index 00000000..551258fe --- /dev/null +++ b/src/tests/stdlib/array_pop.ab @@ -0,0 +1,11 @@ +import { pop, array_first_index } from "std/array" + +main { + let array = ["Failed", "Failed", "Popped"] + if pop(array) == "Popped" + and len(array) == 2 + and array_first_index(array, "Popped") == -1 + { + echo "Succeeded" + } +} diff --git a/src/tests/stdlib/array_shift.ab b/src/tests/stdlib/array_shift.ab new file mode 100644 index 00000000..aeb667f4 --- /dev/null +++ b/src/tests/stdlib/array_shift.ab @@ -0,0 +1,11 @@ +import { shift, array_first_index } from "std/array" + +main { + let array = ["Shifted", "Failed", "Failed"] + if shift(array) == "Shifted" + and len(array) == 2 + and array_first_index(array, "Shifted") == -1 + { + echo "Succeeded" + } +} From f3e9ac59bdfb14590c67035ea713f915be72482f Mon Sep 17 00:00:00 2001 From: Huw Walters Date: Wed, 27 Nov 2024 08:38:46 +0000 Subject: [PATCH 05/13] Use pretty assertions crate. --- Cargo.lock | 23 +++++++++++++++++++++++ Cargo.toml | 1 + src/tests/mod.rs | 4 +++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 71f29069..0910d039 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,6 +27,7 @@ dependencies = [ "include_dir", "itertools", "predicates", + "pretty_assertions", "similar-string", "tempfile", "test-generator", @@ -275,6 +276,12 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "difflib" version = "0.4.0" @@ -503,6 +510,16 @@ dependencies = [ "termtree", ] +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "proc-macro2" version = "0.4.30" @@ -932,3 +949,9 @@ name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" diff --git a/Cargo.toml b/Cargo.toml index dbe10ced..75d68592 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ wildmatch = "2.4.0" [dev-dependencies] assert_cmd = "2.0.14" predicates = "3.1.0" +pretty_assertions = "1.4.1" tempfile = "3.10.1" tiny_http = "0.12.0" diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 1b62c2fb..cb6eb01e 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,6 +1,7 @@ -use crate::compiler::{AmberCompiler, CompilerOptions}; extern crate test_generator; +use crate::compiler::{AmberCompiler, CompilerOptions}; use itertools::Itertools; +use pretty_assertions::assert_eq; use std::fs; use std::path::PathBuf; use std::process::{Command, Stdio}; @@ -82,6 +83,7 @@ pub fn script_test(input: &str) { #[cfg(test)] mod test { use super::*; + use pretty_assertions::assert_eq; #[test] fn test_extract_output() { From 947d197098c9cd7d9563657238b11b1765026e0b Mon Sep 17 00:00:00 2001 From: Huw Walters Date: Wed, 27 Nov 2024 08:57:34 +0000 Subject: [PATCH 06/13] Rename existing array tests. --- src/tests/stdlib/{array_first_index.ab => array_index.ab} | 2 +- src/tests/stdlib/array_search.ab | 4 ++-- .../{array_pass_by_copy.ab => array_assign_by_copy.ab} | 0 .../validity/{array_pass_by_ref.ab => array_assign_by_ref.ab} | 0 src/tests/validity/{array_assign.ab => array_assign_local.ab} | 0 ...rray_assign_out_of_bounds.ab => array_assign_local_end.ab} | 0 .../{array_init.ab => array_init_no_trailing_comma.ab} | 0 7 files changed, 3 insertions(+), 3 deletions(-) rename src/tests/stdlib/{array_first_index.ab => array_index.ab} (96%) rename src/tests/validity/{array_pass_by_copy.ab => array_assign_by_copy.ab} (100%) rename src/tests/validity/{array_pass_by_ref.ab => array_assign_by_ref.ab} (100%) rename src/tests/validity/{array_assign.ab => array_assign_local.ab} (100%) rename src/tests/validity/{array_assign_out_of_bounds.ab => array_assign_local_end.ab} (100%) rename src/tests/validity/{array_init.ab => array_init_no_trailing_comma.ab} (100%) diff --git a/src/tests/stdlib/array_first_index.ab b/src/tests/stdlib/array_index.ab similarity index 96% rename from src/tests/stdlib/array_first_index.ab rename to src/tests/stdlib/array_index.ab index 376131fc..1eb520bc 100644 --- a/src/tests/stdlib/array_first_index.ab +++ b/src/tests/stdlib/array_index.ab @@ -5,4 +5,4 @@ import * from "std/array" main { echo array_first_index([1, 2, 3, 4], 3) -} +} diff --git a/src/tests/stdlib/array_search.ab b/src/tests/stdlib/array_search.ab index e6bf532e..8733a8e0 100644 --- a/src/tests/stdlib/array_search.ab +++ b/src/tests/stdlib/array_search.ab @@ -5,5 +5,5 @@ import * from "std/array" main { let result = array_search([1, 2, 3, 4, 3], 3) - echo result[0]+result[1] -} + echo result[0] + result[1] +} diff --git a/src/tests/validity/array_pass_by_copy.ab b/src/tests/validity/array_assign_by_copy.ab similarity index 100% rename from src/tests/validity/array_pass_by_copy.ab rename to src/tests/validity/array_assign_by_copy.ab diff --git a/src/tests/validity/array_pass_by_ref.ab b/src/tests/validity/array_assign_by_ref.ab similarity index 100% rename from src/tests/validity/array_pass_by_ref.ab rename to src/tests/validity/array_assign_by_ref.ab diff --git a/src/tests/validity/array_assign.ab b/src/tests/validity/array_assign_local.ab similarity index 100% rename from src/tests/validity/array_assign.ab rename to src/tests/validity/array_assign_local.ab diff --git a/src/tests/validity/array_assign_out_of_bounds.ab b/src/tests/validity/array_assign_local_end.ab similarity index 100% rename from src/tests/validity/array_assign_out_of_bounds.ab rename to src/tests/validity/array_assign_local_end.ab diff --git a/src/tests/validity/array_init.ab b/src/tests/validity/array_init_no_trailing_comma.ab similarity index 100% rename from src/tests/validity/array_init.ab rename to src/tests/validity/array_init_no_trailing_comma.ab From a47eadd58b9b2484f6d264118ffbee8139fd54ed Mon Sep 17 00:00:00 2001 From: Huw Walters Date: Wed, 27 Nov 2024 17:43:35 +0000 Subject: [PATCH 07/13] Rename and add array functions; rewrite tests. --- src/std/array.ab | 15 ++++++++--- src/tests/stdlib/array_extract_at.ab | 29 ++++++++++++++++------ src/tests/stdlib/array_first.ab | 17 +++++++++++++ src/tests/stdlib/array_index.ab | 20 ++++++++++++--- src/tests/stdlib/array_last.ab | 15 +++++++++-- src/tests/stdlib/array_pop.ab | 22 ++++++++++------ src/tests/stdlib/array_remove_at.ab | 26 +++++++++++++++---- src/tests/stdlib/array_search.ab | 21 +++++++++++++--- src/tests/stdlib/array_shift.ab | 22 ++++++++++------ src/tests/validity/array_assign_by_copy.ab | 4 +-- src/tests/validity/array_assign_by_ref.ab | 4 +-- 11 files changed, 149 insertions(+), 46 deletions(-) create mode 100644 src/tests/stdlib/array_first.ab diff --git a/src/std/array.ab b/src/std/array.ab index 45e3a9e9..e3b9d337 100644 --- a/src/std/array.ab +++ b/src/std/array.ab @@ -1,7 +1,7 @@ /// Returns index of the first value found in the specified array. /// /// If the value is not found, the function returns -1. -pub fun array_first_index(array, value): Num { +pub fun array_index(array, value): Num { for index, element in array { if value as Text == element as Text { return index @@ -23,13 +23,19 @@ pub fun array_search(array, value): [Num] { /// Checks if a value is in the array. pub fun includes(array, value) { - let result = array_first_index(array, value) + let result = array_index(array, value) return result >= 0 } +/// Returns the first element in the array +pub fun first(array) { + return array[0] +} + /// Returns the last element in the array pub fun last(array) { - return array[len(array) - 1] + let index = len(array) - 1 + return array[index] } /// Removes an element at the index from the array @@ -46,7 +52,8 @@ pub fun extract_at(ref array, index) { /// Removes the last element from the array, and returns it pub fun pop(ref array) { - return extract_at(array, len(array) - 1) + let index = len(array) - 1 + return extract_at(array, index) } /// Removes the first element from the array, and returns it diff --git a/src/tests/stdlib/array_extract_at.ab b/src/tests/stdlib/array_extract_at.ab index 606fe989..642052c6 100644 --- a/src/tests/stdlib/array_extract_at.ab +++ b/src/tests/stdlib/array_extract_at.ab @@ -1,12 +1,25 @@ -import { extract_at, array_first_index } from "std/array" +import { extract_at } from "std/array" -main { - let array = ["Failed", "Extracted", "Failed"] +// Output +// Value at -5: "", [apple banana cherry date] +// Value at -4: "apple", [apple banana cherry date] +// Value at -3: "banana", [apple banana cherry date] +// Value at -2: "cherry", [apple banana cherry date] +// Value at -1: "date", [apple banana cherry date] +// Value at 0: "apple", [banana cherry date] +// Value at 1: "banana", [apple cherry date] +// Value at 2: "cherry", [apple banana date] +// Value at 3: "date", [apple banana cherry] +// Value at 4: "", [apple banana cherry date] + +fun test_extract(inner: [Text], index: Num): Null { + let value = extract_at(inner, index) + echo "Value at {index}: \"{value}\", [{inner}]" +} - if extract_at(array, 1) == "Extracted" - and len(array) == 2 - and array_first_index(array, "Extracted") == -1 - { - echo "Succeeded" +main { + let data = ["apple", "banana", "cherry", "date"] + for index in -5..=4 { + test_extract(data, index) } } diff --git a/src/tests/stdlib/array_first.ab b/src/tests/stdlib/array_first.ab new file mode 100644 index 00000000..59eb3863 --- /dev/null +++ b/src/tests/stdlib/array_first.ab @@ -0,0 +1,17 @@ +import { first } from "std/array" + +// Output +// First of array: "apple", [apple banana cherry date] +// First of empty: "", [] + +fun test_first(label: Text, array: [Text]): Null { + let value = first(array) + echo "First of {label}: \"{value}\", [{array}]" +} + +main { + let array = ["apple", "banana", "cherry", "date"] + let empty = [Text] + test_first("array", array) + test_first("empty", empty) +} diff --git a/src/tests/stdlib/array_index.ab b/src/tests/stdlib/array_index.ab index 1eb520bc..c4276033 100644 --- a/src/tests/stdlib/array_index.ab +++ b/src/tests/stdlib/array_index.ab @@ -1,8 +1,22 @@ -import * from "std/array" +import { array_index } from "std/array" // Output -// 2 +// Index of "apple": 0 +// Index of "banana": 1 +// Index of "cherry": 2 +// Index of "date": 3 +// Index of "kiwi": -1 + +fun test_index(array: [Text], value: Text): Null { + let index = array_index(array, value) + echo "Index of \"{value}\": {index}" +} main { - echo array_first_index([1, 2, 3, 4], 3) + let array = ["apple", "banana", "cherry", "date", "cherry", "banana", "apple"] + test_index(array, "apple") + test_index(array, "banana") + test_index(array, "cherry") + test_index(array, "date") + test_index(array, "kiwi") } diff --git a/src/tests/stdlib/array_last.ab b/src/tests/stdlib/array_last.ab index d00e2000..6e56401a 100644 --- a/src/tests/stdlib/array_last.ab +++ b/src/tests/stdlib/array_last.ab @@ -1,6 +1,17 @@ import { last } from "std/array" +// Output +// Last of array: "date", [apple banana cherry date] +// Last of empty: "", [] + +fun test_last(label: Text, array: [Text]): Null { + let value = last(array) + echo "Last of {label}: \"{value}\", [{array}]" +} + main { - let array = ["Failed", "Not", "Succeeded"] - echo last(array) + let array = ["apple", "banana", "cherry", "date"] + let empty = [Text] + test_last("array", array) + test_last("empty", empty) } diff --git a/src/tests/stdlib/array_pop.ab b/src/tests/stdlib/array_pop.ab index 551258fe..91a3ee6a 100644 --- a/src/tests/stdlib/array_pop.ab +++ b/src/tests/stdlib/array_pop.ab @@ -1,11 +1,17 @@ -import { pop, array_first_index } from "std/array" +import { pop } from "std/array" + +// Output +// Popped from data: "date", [apple banana cherry] +// Popped from empty: "", [] + +fun test_pop(label: Text, inner: [Text]): Null { + let value = pop(inner) + echo "Popped from {label}: \"{value}\", [{inner}]" +} main { - let array = ["Failed", "Failed", "Popped"] - if pop(array) == "Popped" - and len(array) == 2 - and array_first_index(array, "Popped") == -1 - { - echo "Succeeded" - } + let data = ["apple", "banana", "cherry", "date"] + let empty = [Text] + test_pop("data", data) + test_pop("empty", empty) } diff --git a/src/tests/stdlib/array_remove_at.ab b/src/tests/stdlib/array_remove_at.ab index 4c32469d..9ae4a611 100644 --- a/src/tests/stdlib/array_remove_at.ab +++ b/src/tests/stdlib/array_remove_at.ab @@ -1,9 +1,25 @@ -import { remove_at, array_first_index } from "std/array" +import { remove_at } from "std/array" + +// Output +// Array after -5: [apple banana cherry date] +// Array after -4: [apple banana cherry date] +// Array after -3: [apple banana cherry date] +// Array after -2: [apple banana cherry date] +// Array after -1: [apple banana cherry date] +// Array after 0: [banana cherry date] +// Array after 1: [apple cherry date] +// Array after 2: [apple banana date] +// Array after 3: [apple banana cherry] +// Array after 4: [apple banana cherry date] + +fun test_remove(inner: [Text], index: Num): Null { + let value = remove_at(inner, index) + echo "Array after {index}: [{inner}]" +} main { - let array = ["Failed", "Removed", "Failed"] - remove_at(array, 1) - if len(array) == 2 and array_first_index(array, "Removed") == -1 { - echo "Succeeded" + let data = ["apple", "banana", "cherry", "date"] + for index in -5..=4 { + test_remove(data, index) } } diff --git a/src/tests/stdlib/array_search.ab b/src/tests/stdlib/array_search.ab index 8733a8e0..bee14fd3 100644 --- a/src/tests/stdlib/array_search.ab +++ b/src/tests/stdlib/array_search.ab @@ -1,9 +1,22 @@ -import * from "std/array" +import { array_search } from "std/array" // Output -// 6 +// Indices of "apple": [0 6] +// Indices of "banana": [1 5] +// Indices of "cherry": [2 4] +// Indices of "date": [3] +// Indices of "kiwi": [] + +fun test_search(array: [Text], value: Text): Null { + let indices = array_search(array, value) + echo "Indices of \"{value}\": [{indices}]" +} main { - let result = array_search([1, 2, 3, 4, 3], 3) - echo result[0] + result[1] + let array = ["apple", "banana", "cherry", "date", "cherry", "banana", "apple"] + test_search(array, "apple") + test_search(array, "banana") + test_search(array, "cherry") + test_search(array, "date") + test_search(array, "kiwi") } diff --git a/src/tests/stdlib/array_shift.ab b/src/tests/stdlib/array_shift.ab index aeb667f4..60713578 100644 --- a/src/tests/stdlib/array_shift.ab +++ b/src/tests/stdlib/array_shift.ab @@ -1,11 +1,17 @@ -import { shift, array_first_index } from "std/array" +import { shift } from "std/array" + +// Output +// Shifted from data: "apple", [banana cherry date] +// Shifted from empty: "", [] + +fun test_shift(label: Text, inner: [Text]): Null { + let value = shift(inner) + echo "Shifted from {label}: \"{value}\", [{inner}]" +} main { - let array = ["Shifted", "Failed", "Failed"] - if shift(array) == "Shifted" - and len(array) == 2 - and array_first_index(array, "Shifted") == -1 - { - echo "Succeeded" - } + let data = ["apple", "banana", "cherry", "date"] + let empty = [Text] + test_shift("data", data) + test_shift("empty", empty) } diff --git a/src/tests/validity/array_assign_by_copy.ab b/src/tests/validity/array_assign_by_copy.ab index 17f4b20d..c3b0d64b 100644 --- a/src/tests/validity/array_assign_by_copy.ab +++ b/src/tests/validity/array_assign_by_copy.ab @@ -1,10 +1,10 @@ // Output -// 42 +// 1 2 42 4 5 // 1 2 3 4 5 fun test(a) { a[2] = 42 - echo a[2] + echo a } let a = [1, 2, 3, 4, 5] diff --git a/src/tests/validity/array_assign_by_ref.ab b/src/tests/validity/array_assign_by_ref.ab index c3a0f54e..e590a078 100644 --- a/src/tests/validity/array_assign_by_ref.ab +++ b/src/tests/validity/array_assign_by_ref.ab @@ -1,10 +1,10 @@ // Output -// 2 +// 1 2 42 4 5 // 1 2 42 4 5 fun test(ref a) { a[2] = 42 - echo a[1] + echo a } let a = [1, 2, 3, 4, 5] From 89cce1a6db6d7add805c290689720719b5584c38 Mon Sep 17 00:00:00 2001 From: Huw Walters Date: Fri, 29 Nov 2024 19:32:24 +0000 Subject: [PATCH 08/13] Refactor variable translation; optimise imports. --- src/modules/expression/binop/range.rs | 10 ++--- src/modules/expression/unop/neg.rs | 11 +++-- src/modules/variable/get.rs | 59 +++++++++++++++++---------- src/modules/variable/mod.rs | 9 ++-- 4 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/modules/expression/binop/range.rs b/src/modules/expression/binop/range.rs index 7120a4ff..eb10ea48 100644 --- a/src/modules/expression/binop/range.rs +++ b/src/modules/expression/binop/range.rs @@ -1,14 +1,14 @@ -use std::ops::Sub; -use heraclitus_compiler::prelude::*; use crate::docs::module::DocumentationModule; +use crate::modules::expression::binop::BinOp; use crate::modules::expression::expr::{Expr, ExprType}; use crate::modules::types::{Type, Typed}; -use crate::utils::metadata::ParserMetadata; use crate::translate::compute::{translate_computation, ArithOp}; use crate::translate::module::TranslateModule; +use crate::utils::metadata::ParserMetadata; use crate::utils::TranslateMetadata; -use crate::{handle_binop, error_type_match}; -use super::BinOp; +use crate::{error_type_match, handle_binop}; +use heraclitus_compiler::prelude::*; +use std::ops::Sub; #[derive(Debug, Clone)] pub struct Range { diff --git a/src/modules/expression/unop/neg.rs b/src/modules/expression/unop/neg.rs index 2c7faec5..5eddc34f 100644 --- a/src/modules/expression/unop/neg.rs +++ b/src/modules/expression/unop/neg.rs @@ -1,8 +1,13 @@ -use heraclitus_compiler::prelude::*; -use crate::{utils::{metadata::ParserMetadata, TranslateMetadata}, modules::types::{Type, Typed}, translate::{module::TranslateModule, compute::{translate_computation, ArithOp}}}; -use super::{super::expr::Expr, UnOp}; use crate::docs::module::DocumentationModule; use crate::error_type_match; +use crate::modules::expression::expr::Expr; +use crate::modules::expression::unop::UnOp; +use crate::modules::types::{Type, Typed}; +use crate::translate::compute::{translate_computation, ArithOp}; +use crate::translate::module::TranslateModule; +use crate::utils::metadata::ParserMetadata; +use crate::utils::TranslateMetadata; +use heraclitus_compiler::prelude::*; #[derive(Debug, Clone)] pub struct Neg { diff --git a/src/modules/variable/get.rs b/src/modules/variable/get.rs index 0f1b8a6d..e8db9f3d 100644 --- a/src/modules/variable/get.rs +++ b/src/modules/variable/get.rs @@ -1,7 +1,10 @@ -use heraclitus_compiler::prelude::*; -use crate::{docs::module::DocumentationModule, modules::{expression::expr::Expr, types::{Type, Typed}}, utils::{ParserMetadata, TranslateMetadata}}; +use crate::docs::module::DocumentationModule; +use crate::modules::expression::expr::Expr; +use crate::modules::types::{Type, Typed}; +use crate::modules::variable::{handle_index_accessor, handle_variable_reference, variable_name_extensions}; use crate::translate::module::TranslateModule; -use super::{variable_name_extensions, handle_variable_reference, handle_index_accessor}; +use crate::utils::{ParserMetadata, TranslateMetadata}; +use heraclitus_compiler::prelude::*; #[derive(Debug, Clone)] pub struct VariableGet { @@ -63,27 +66,41 @@ impl SyntaxModule for VariableGet { impl TranslateModule for VariableGet { fn translate(&self, meta: &mut TranslateMetadata) -> String { let name = self.get_translated_name(); - let ref_prefix = if self.is_ref { "!" } else { "" }; - let res = format!("${{{ref_prefix}{name}}}"); // Text variables need to be encapsulated in string literals // Otherwise, they will be "spread" into tokens let quote = meta.gen_quote(); - match (self.is_ref, &self.kind) { - (false, Type::Array(_)) => match *self.index { - Some(ref expr) => format!("{quote}${{{name}[{}]}}{quote}", expr.translate(meta)), - None => format!("{quote}${{{name}[@]}}{quote}") - }, - (true, Type::Array(_)) => match *self.index { - Some(ref expr) => { - let id = meta.gen_value_id(); - let expr = expr.translate_eval(meta, true); - meta.stmt_queue.push_back(format!("eval \"local __AMBER_ARRAY_GET_{id}_{name}=\\\"\\${{${name}[{expr}]}}\\\"\"")); - format!("$__AMBER_ARRAY_GET_{id}_{name}") // echo $__ARRAY_GET - }, - None => format!("{quote}${{!__AMBER_ARRAY_{name}}}{quote}") - }, - (_, Type::Text) => format!("{quote}{res}{quote}"), - _ => res + match &self.kind { + Type::Array(_) => { + if self.is_ref { + if let Some(index) = self.index.as_ref() { + let value = { + let index = index.translate_eval(meta, true); + format!("\\\"\\${{${name}[{index}]}}\\\"") + }; + let id = meta.gen_value_id(); + let stmt = format!("eval \"local __AMBER_ARRAY_GET_{id}_{name}={value}\""); + meta.stmt_queue.push_back(stmt); + format!("$__AMBER_ARRAY_GET_{id}_{name}") // echo $__ARRAY_GET + } else { + format!("{quote}${{!__AMBER_ARRAY_{name}}}{quote}") + } + } else { + if let Some(index) = self.index.as_ref() { + let index = index.translate(meta); + format!("{quote}${{{name}[{index}]}}{quote}") + } else { + format!("{quote}${{{name}[@]}}{quote}") + } + } + } + Type::Text => { + let ref_prefix = if self.is_ref { "!" } else { "" }; + format!("{quote}${{{ref_prefix}{name}}}{quote}") + } + _ => { + let ref_prefix = if self.is_ref { "!" } else { "" }; + format!("${{{ref_prefix}{name}}}") + } } } } diff --git a/src/modules/variable/mod.rs b/src/modules/variable/mod.rs index a8a20358..bab6af14 100644 --- a/src/modules/variable/mod.rs +++ b/src/modules/variable/mod.rs @@ -1,9 +1,10 @@ +use crate::modules::expression::expr::Expr; +use crate::modules::types::{Type, Typed}; +use crate::utils::cc_flags::{get_ccflag_name, CCFlags}; +use crate::utils::context::VariableDecl; +use crate::utils::metadata::ParserMetadata; use heraclitus_compiler::prelude::*; -use crate::utils::{metadata::ParserMetadata, context::VariableDecl, cc_flags::{get_ccflag_name, CCFlags}}; use similar_string::find_best_similarity; -use crate::modules::types::{Typed, Type}; - -use super::expression::expr::Expr; pub mod init; pub mod set; From 6cb2d12d53585f47fe5f26ff22376eac0227e275 Mon Sep 17 00:00:00 2001 From: Huw Walters Date: Fri, 29 Nov 2024 21:52:47 +0000 Subject: [PATCH 09/13] Refactor array index by negative number. --- src/modules/expression/expr.rs | 8 ++++++ src/modules/expression/literal/number.rs | 7 +++++ src/modules/expression/unop/neg.rs | 16 ++++++++++++ src/modules/variable/get.rs | 26 ++++++++++++++----- src/tests/validity/array_get_index_by_copy.ab | 22 ++++++++++++++++ src/tests/validity/array_get_index_by_ref.ab | 22 ++++++++++++++++ src/tests/validity/array_get_index_local.ab | 25 ++++++++++++++++++ 7 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 src/tests/validity/array_get_index_by_copy.ab create mode 100644 src/tests/validity/array_get_index_by_ref.ab create mode 100644 src/tests/validity/array_get_index_local.ab diff --git a/src/modules/expression/expr.rs b/src/modules/expression/expr.rs index 6eff56f5..fd071418 100644 --- a/src/modules/expression/expr.rs +++ b/src/modules/expression/expr.rs @@ -101,6 +101,14 @@ impl Typed for Expr { } impl Expr { + pub fn get_integer_value(&self) -> Option { + match &self.value { + Some(ExprType::Number(value)) => value.get_integer_value(), + Some(ExprType::Neg(value)) => value.get_integer_value(), + _ => None, + } + } + pub fn get_position(&self, meta: &mut ParserMetadata) -> PositionInfo { let begin = meta.get_token_at(self.pos.0); let end = meta.get_token_at(self.pos.1); diff --git a/src/modules/expression/literal/number.rs b/src/modules/expression/literal/number.rs index d20e108c..b2deda3f 100644 --- a/src/modules/expression/literal/number.rs +++ b/src/modules/expression/literal/number.rs @@ -46,6 +46,13 @@ impl TranslateModule for Number { } } +impl Number { + pub fn get_integer_value(&self) -> Option { + let value = self.value.parse().unwrap_or_default(); + Some(value) + } +} + impl DocumentationModule for Number { fn document(&self, _meta: &ParserMetadata) -> String { "".to_string() diff --git a/src/modules/expression/unop/neg.rs b/src/modules/expression/unop/neg.rs index 5eddc34f..8bb171a9 100644 --- a/src/modules/expression/unop/neg.rs +++ b/src/modules/expression/unop/neg.rs @@ -8,6 +8,7 @@ use crate::translate::module::TranslateModule; use crate::utils::metadata::ParserMetadata; use crate::utils::TranslateMetadata; use heraclitus_compiler::prelude::*; +use std::ops::Neg as _; #[derive(Debug, Clone)] pub struct Neg { @@ -56,6 +57,21 @@ impl TranslateModule for Neg { } } +impl Neg { + pub fn get_integer_value(&self) -> Option { + self.expr.get_integer_value().map(isize::neg) + } + + pub fn get_array_index(&self, meta: &mut TranslateMetadata) -> String { + if let Some(expr) = self.get_integer_value() { + expr.to_string() + } else { + let expr = self.expr.translate(meta); + translate_computation(meta, ArithOp::Neg, None, Some(expr)) + } + } +} + impl DocumentationModule for Neg { fn document(&self, _meta: &ParserMetadata) -> String { "".to_string() diff --git a/src/modules/variable/get.rs b/src/modules/variable/get.rs index e8db9f3d..9c6dc7cb 100644 --- a/src/modules/variable/get.rs +++ b/src/modules/variable/get.rs @@ -1,5 +1,5 @@ use crate::docs::module::DocumentationModule; -use crate::modules::expression::expr::Expr; +use crate::modules::expression::expr::{Expr, ExprType}; use crate::modules::types::{Type, Typed}; use crate::modules::variable::{handle_index_accessor, handle_variable_reference, variable_name_extensions}; use crate::translate::module::TranslateModule; @@ -73,9 +73,15 @@ impl TranslateModule for VariableGet { Type::Array(_) => { if self.is_ref { if let Some(index) = self.index.as_ref() { - let value = { - let index = index.translate_eval(meta, true); - format!("\\\"\\${{${name}[{index}]}}\\\"") + let value = match &index.value { + Some(ExprType::Neg(neg)) => { + let index = neg.get_array_index(meta); + format!("\\\"\\${{${name}[{index}]}}\\\"") + } + _ => { + let index = index.translate_eval(meta, true); + format!("\\\"\\${{${name}[{index}]}}\\\"") + } }; let id = meta.gen_value_id(); let stmt = format!("eval \"local __AMBER_ARRAY_GET_{id}_{name}={value}\""); @@ -86,8 +92,16 @@ impl TranslateModule for VariableGet { } } else { if let Some(index) = self.index.as_ref() { - let index = index.translate(meta); - format!("{quote}${{{name}[{index}]}}{quote}") + match &index.value { + Some(ExprType::Neg(neg)) => { + let index = neg.get_array_index(meta); + format!("{quote}${{{name}[{index}]}}{quote}") + } + _ => { + let index = index.translate(meta); + format!("{quote}${{{name}[{index}]}}{quote}") + } + } } else { format!("{quote}${{{name}[@]}}{quote}") } diff --git a/src/tests/validity/array_get_index_by_copy.ab b/src/tests/validity/array_get_index_by_copy.ab new file mode 100644 index 00000000..422a5b27 --- /dev/null +++ b/src/tests/validity/array_get_index_by_copy.ab @@ -0,0 +1,22 @@ +// Output +// Value at -5: "" +// Value at -4: "apple" +// Value at -3: "banana" +// Value at -2: "cherry" +// Value at -1: "date" +// Value at 0: "apple" +// Value at 1: "banana" +// Value at 2: "cherry" +// Value at 3: "date" +// Value at 4: "" + +fun test_index(inner) { + for index in -5..=4 { + echo "Value at {index}: \"{inner[index]}\"" + } +} + +main { + let array = ["apple", "banana", "cherry", "date"] + test_index(array) +} diff --git a/src/tests/validity/array_get_index_by_ref.ab b/src/tests/validity/array_get_index_by_ref.ab new file mode 100644 index 00000000..3130c078 --- /dev/null +++ b/src/tests/validity/array_get_index_by_ref.ab @@ -0,0 +1,22 @@ +// Output +// Value at -5: "" +// Value at -4: "apple" +// Value at -3: "banana" +// Value at -2: "cherry" +// Value at -1: "date" +// Value at 0: "apple" +// Value at 1: "banana" +// Value at 2: "cherry" +// Value at 3: "date" +// Value at 4: "" + +fun test_index(ref inner) { + for index in -5..=4 { + echo "Value at {index}: \"{inner[index]}\"" + } +} + +main { + let array = ["apple", "banana", "cherry", "date"] + test_index(array) +} diff --git a/src/tests/validity/array_get_index_local.ab b/src/tests/validity/array_get_index_local.ab new file mode 100644 index 00000000..fc85c30f --- /dev/null +++ b/src/tests/validity/array_get_index_local.ab @@ -0,0 +1,25 @@ +// Output +// Value at -5: "" +// Value at -4: "apple" +// Value at -3: "banana" +// Value at -2: "cherry" +// Value at -1: "date" +// Value at 0: "apple" +// Value at 1: "banana" +// Value at 2: "cherry" +// Value at 3: "date" +// Value at 4: "" + +main { + let array = ["apple", "banana", "cherry", "date"] + echo "Value at -5: \"{array[-5]}\"" + echo "Value at -4: \"{array[-4]}\"" + echo "Value at -3: \"{array[-3]}\"" + echo "Value at -2: \"{array[-2]}\"" + echo "Value at -1: \"{array[-1]}\"" + echo "Value at 0: \"{array[0]}\"" + echo "Value at 1: \"{array[1]}\"" + echo "Value at 2: \"{array[2]}\"" + echo "Value at 3: \"{array[3]}\"" + echo "Value at 4: \"{array[4]}\"" +} From b31d4fff3733d6c9106b0860cedadba6d4fb8b8b Mon Sep 17 00:00:00 2001 From: Huw Walters Date: Fri, 29 Nov 2024 22:34:41 +0000 Subject: [PATCH 10/13] Support array index by range. --- src/modules/expression/binop/range.rs | 63 +++++++-- src/modules/variable/get.rs | 30 ++++- src/modules/variable/mod.rs | 22 ++- src/modules/variable/set.rs | 2 +- src/tests/validity/array_get_range_by_copy.ab | 79 +++++++++++ src/tests/validity/array_get_range_by_ref.ab | 79 +++++++++++ src/tests/validity/array_get_range_local.ab | 125 ++++++++++++++++++ 7 files changed, 377 insertions(+), 23 deletions(-) create mode 100644 src/tests/validity/array_get_range_by_copy.ab create mode 100644 src/tests/validity/array_get_range_by_ref.ab create mode 100644 src/tests/validity/array_get_range_local.ab diff --git a/src/modules/expression/binop/range.rs b/src/modules/expression/binop/range.rs index eb10ea48..f592656f 100644 --- a/src/modules/expression/binop/range.rs +++ b/src/modules/expression/binop/range.rs @@ -1,6 +1,6 @@ use crate::docs::module::DocumentationModule; use crate::modules::expression::binop::BinOp; -use crate::modules::expression::expr::{Expr, ExprType}; +use crate::modules::expression::expr::Expr; use crate::modules::types::{Type, Typed}; use crate::translate::compute::{translate_computation, ArithOp}; use crate::translate::module::TranslateModule; @@ -8,7 +8,7 @@ use crate::utils::metadata::ParserMetadata; use crate::utils::TranslateMetadata; use crate::{error_type_match, handle_binop}; use heraclitus_compiler::prelude::*; -use std::ops::Sub; +use std::cmp::max; #[derive(Debug, Clone)] pub struct Range { @@ -59,17 +59,60 @@ impl SyntaxModule for Range { impl TranslateModule for Range { fn translate(&self, meta: &mut TranslateMetadata) -> String { let from = self.from.translate(meta); - let to = self.to.translate(meta); - if self.neq { - let to_neq = if let Some(ExprType::Number(_)) = &self.to.value { - to.parse::().unwrap_or_default().sub(1).to_string() + let to = if let Some(to) = self.to.get_integer_value() { + if self.neq { + (to - 1).to_string() } else { - translate_computation(meta, ArithOp::Sub, Some(to), Some("1".to_string())) - }; - meta.gen_subprocess(&format!("seq {} {}", from, to_neq)) + to.to_string() + } } else { - meta.gen_subprocess(&format!("seq {} {}", from, to)) + let to = self.to.translate(meta); + if self.neq { + translate_computation(meta, ArithOp::Sub, Some(to), Some("1".to_string())) + } else { + to + } + }; + let stmt = format!("seq {} {}", from, to); + meta.gen_subprocess(&stmt) + } +} + +impl Range { + pub fn get_array_index(&self, meta: &mut TranslateMetadata) -> (String, String) { + if let Some(from) = self.from.get_integer_value() { + if let Some(mut to) = self.to.get_integer_value() { + // Make the upper bound exclusive. + if !self.neq { + to = to + 1; + } + // Cap the lower bound at zero. + let offset = max(from, 0); + // Cap the slice length at zero. + let length = max(to - offset, 0); + return (offset.to_string(), length.to_string()); + } + } + let local = if meta.fun_meta.is_some() { "local " } else { "" }; + // Make the upper bound exclusive. + let upper_name = format!("__SLICE_UPPER_{}", meta.gen_value_id()); + let mut upper_val = self.to.translate(meta); + if !self.neq { + upper_val = translate_computation(meta, ArithOp::Add, Some(upper_val), Some("1".to_string())); } + meta.stmt_queue.push_back(format!("{local}{upper_name}={upper_val}")); + // Cap the lower bound at zero. + let offset_name = format!("__SLICE_OFFSET_{}", meta.gen_value_id()); + let offset_val = self.from.translate(meta); + meta.stmt_queue.push_back(format!("{local}{offset_name}={offset_val}")); + meta.stmt_queue.push_back(format!("{offset_name}=$(({offset_name} > 0 ? {offset_name} : 0))")); + let offset_val = format!("${offset_name}"); + // Cap the slice length at zero. + let length_name = format!("__SLICE_LENGTH_{}", meta.gen_value_id()); + let length_val = translate_computation(meta, ArithOp::Sub, Some(upper_val), Some(offset_val)); + meta.stmt_queue.push_back(format!("{local}{length_name}={length_val}")); + meta.stmt_queue.push_back(format!("{length_name}=$(({length_name} > 0 ? {length_name} : 0))")); + (format!("${offset_name}"), format!("${length_name}")) } } diff --git a/src/modules/variable/get.rs b/src/modules/variable/get.rs index 9c6dc7cb..9dcfbbaf 100644 --- a/src/modules/variable/get.rs +++ b/src/modules/variable/get.rs @@ -26,10 +26,22 @@ impl VariableGet { impl Typed for VariableGet { fn get_type(&self) -> Type { - match (&*self.index, self.kind.clone()) { - // Return the type of the array element if indexed - (Some(_), Type::Array(kind)) => *kind, - _ => self.kind.clone() + if let Type::Array(item_kind) = &self.kind { + if let Some(index) = self.index.as_ref() { + if let Some(ExprType::Range(_)) = &index.value { + // Array type (indexing array by range) + self.kind.clone() + } else { + // Item type (indexing array by number) + *item_kind.clone() + } + } else { + // Array type (returning array) + self.kind.clone() + } + } else { + // Variable type (returning text or number) + self.kind.clone() } } } @@ -54,7 +66,7 @@ impl SyntaxModule for VariableGet { self.global_id = variable.global_id; self.is_ref = variable.is_ref; self.kind = variable.kind.clone(); - self.index = Box::new(handle_index_accessor(meta)?); + self.index = Box::new(handle_index_accessor(meta, true)?); // Check if the variable can be indexed if self.index.is_some() && !matches!(variable.kind, Type::Array(_)) { return error!(meta, tok, format!("Cannot index a non-array variable of type '{}'", self.kind)); @@ -74,6 +86,10 @@ impl TranslateModule for VariableGet { if self.is_ref { if let Some(index) = self.index.as_ref() { let value = match &index.value { + Some(ExprType::Range(range)) => { + let (offset, length) = range.get_array_index(meta); + format!("\\\"\\${{${name}[@]:{offset}:{length}}}\\\"") + } Some(ExprType::Neg(neg)) => { let index = neg.get_array_index(meta); format!("\\\"\\${{${name}[{index}]}}\\\"") @@ -93,6 +109,10 @@ impl TranslateModule for VariableGet { } else { if let Some(index) = self.index.as_ref() { match &index.value { + Some(ExprType::Range(range)) => { + let (offset, length) = range.get_array_index(meta); + format!("{quote}${{{name}[@]:{offset}:{length}}}{quote}") + } Some(ExprType::Neg(neg)) => { let index = neg.get_array_index(meta); format!("{quote}${{{name}[{index}]}}{quote}") diff --git a/src/modules/variable/mod.rs b/src/modules/variable/mod.rs index bab6af14..780875f0 100644 --- a/src/modules/variable/mod.rs +++ b/src/modules/variable/mod.rs @@ -1,4 +1,4 @@ -use crate::modules::expression::expr::Expr; +use crate::modules::expression::expr::{Expr, ExprType}; use crate::modules::types::{Type, Typed}; use crate::utils::cc_flags::{get_ccflag_name, CCFlags}; use crate::utils::context::VariableDecl; @@ -104,19 +104,27 @@ fn is_camel_case(name: &str) -> bool { false } -pub fn handle_index_accessor(meta: &mut ParserMetadata) -> Result, Failure> { +pub fn handle_index_accessor(meta: &mut ParserMetadata, range: bool) -> Result, Failure> { if token(meta, "[").is_ok() { let tok = meta.get_current_token(); let mut index = Expr::new(); syntax(meta, &mut index)?; - if index.get_type() != Type::Num { - return error!(meta, tok => { - message: format!("Index accessor must be a number"), - comment: format!("The index accessor must be a number, not a {}", index.get_type()) - }) + if !allow_index_accessor(&index, range) { + let expected = if range { "number or range" } else { "number" }; + let message = format!("Index accessor must be a {}", expected); + let comment = format!("The index accessor must be a {} not a {}", expected, index.get_type()); + return error!(meta, tok => { message: message, comment: comment }); } token(meta, "]")?; return Ok(Some(index)); } Ok(None) } + +fn allow_index_accessor(index: &Expr, range: bool) -> bool { + match (&index.kind, &index.value) { + (Type::Num, _) => true, + (Type::Array(_), Some(ExprType::Range(_))) => range, + _ => false, + } +} diff --git a/src/modules/variable/set.rs b/src/modules/variable/set.rs index 223da82d..0f1c3c64 100644 --- a/src/modules/variable/set.rs +++ b/src/modules/variable/set.rs @@ -40,7 +40,7 @@ impl SyntaxModule for VariableSet { fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { let tok = meta.get_current_token(); self.name = variable(meta, variable_name_extensions())?; - self.index = handle_index_accessor(meta)?; + self.index = handle_index_accessor(meta, false)?; token(meta, "=")?; syntax(meta, &mut *self.expr)?; let variable = handle_variable_reference(meta, tok.clone(), &self.name)?; diff --git a/src/tests/validity/array_get_range_by_copy.ab b/src/tests/validity/array_get_range_by_copy.ab new file mode 100644 index 00000000..442ec579 --- /dev/null +++ b/src/tests/validity/array_get_range_by_copy.ab @@ -0,0 +1,79 @@ +// Output +// Values at -1..0: [] +// Values at -1..1: [apple] +// Values at -1..2: [apple banana] +// Values at -1..3: [apple banana cherry] +// Values at -1..4: [apple banana cherry] +// Values at 0..0: [] +// Values at 0..1: [apple] +// Values at 0..2: [apple banana] +// Values at 0..3: [apple banana cherry] +// Values at 0..4: [apple banana cherry] +// Values at 1..0: [] +// Values at 1..1: [] +// Values at 1..2: [banana] +// Values at 1..3: [banana cherry] +// Values at 1..4: [banana cherry] +// Values at 2..0: [] +// Values at 2..1: [] +// Values at 2..2: [] +// Values at 2..3: [cherry] +// Values at 2..4: [cherry] +// Values at 3..0: [] +// Values at 3..1: [] +// Values at 3..2: [] +// Values at 3..3: [] +// Values at 3..4: [] +// Values at 4..0: [] +// Values at 4..1: [] +// Values at 4..2: [] +// Values at 4..3: [] +// Values at 4..4: [] +// Values at -1..=-1: [] +// Values at -1..=0: [apple] +// Values at -1..=1: [apple banana] +// Values at -1..=2: [apple banana cherry] +// Values at -1..=3: [apple banana cherry] +// Values at 0..=-1: [] +// Values at 0..=0: [apple] +// Values at 0..=1: [apple banana] +// Values at 0..=2: [apple banana cherry] +// Values at 0..=3: [apple banana cherry] +// Values at 1..=-1: [] +// Values at 1..=0: [] +// Values at 1..=1: [banana] +// Values at 1..=2: [banana cherry] +// Values at 1..=3: [banana cherry] +// Values at 2..=-1: [] +// Values at 2..=0: [] +// Values at 2..=1: [] +// Values at 2..=2: [cherry] +// Values at 2..=3: [cherry] +// Values at 3..=-1: [] +// Values at 3..=0: [] +// Values at 3..=1: [] +// Values at 3..=2: [] +// Values at 3..=3: [] +// Values at 4..=-1: [] +// Values at 4..=0: [] +// Values at 4..=1: [] +// Values at 4..=2: [] +// Values at 4..=3: [] + +fun test_range(array) { + for begin in -1..=4 { + for end in 0..=4 { + echo "Values at {begin}..{end}: [{array[begin..end]}]" + } + } + for begin in -1..=4 { + for end in -1..=3 { + echo "Values at {begin}..={end}: [{array[begin..=end]}]" + } + } +} + +main { + let data = ["apple", "banana", "cherry"] + test_range(data) +} diff --git a/src/tests/validity/array_get_range_by_ref.ab b/src/tests/validity/array_get_range_by_ref.ab new file mode 100644 index 00000000..846a3170 --- /dev/null +++ b/src/tests/validity/array_get_range_by_ref.ab @@ -0,0 +1,79 @@ +// Output +// Values at -1..0: [] +// Values at -1..1: [apple] +// Values at -1..2: [apple banana] +// Values at -1..3: [apple banana cherry] +// Values at -1..4: [apple banana cherry] +// Values at 0..0: [] +// Values at 0..1: [apple] +// Values at 0..2: [apple banana] +// Values at 0..3: [apple banana cherry] +// Values at 0..4: [apple banana cherry] +// Values at 1..0: [] +// Values at 1..1: [] +// Values at 1..2: [banana] +// Values at 1..3: [banana cherry] +// Values at 1..4: [banana cherry] +// Values at 2..0: [] +// Values at 2..1: [] +// Values at 2..2: [] +// Values at 2..3: [cherry] +// Values at 2..4: [cherry] +// Values at 3..0: [] +// Values at 3..1: [] +// Values at 3..2: [] +// Values at 3..3: [] +// Values at 3..4: [] +// Values at 4..0: [] +// Values at 4..1: [] +// Values at 4..2: [] +// Values at 4..3: [] +// Values at 4..4: [] +// Values at -1..=-1: [] +// Values at -1..=0: [apple] +// Values at -1..=1: [apple banana] +// Values at -1..=2: [apple banana cherry] +// Values at -1..=3: [apple banana cherry] +// Values at 0..=-1: [] +// Values at 0..=0: [apple] +// Values at 0..=1: [apple banana] +// Values at 0..=2: [apple banana cherry] +// Values at 0..=3: [apple banana cherry] +// Values at 1..=-1: [] +// Values at 1..=0: [] +// Values at 1..=1: [banana] +// Values at 1..=2: [banana cherry] +// Values at 1..=3: [banana cherry] +// Values at 2..=-1: [] +// Values at 2..=0: [] +// Values at 2..=1: [] +// Values at 2..=2: [cherry] +// Values at 2..=3: [cherry] +// Values at 3..=-1: [] +// Values at 3..=0: [] +// Values at 3..=1: [] +// Values at 3..=2: [] +// Values at 3..=3: [] +// Values at 4..=-1: [] +// Values at 4..=0: [] +// Values at 4..=1: [] +// Values at 4..=2: [] +// Values at 4..=3: [] + +fun test_range(ref array) { + for begin in -1..=4 { + for end in 0..=4 { + echo "Values at {begin}..{end}: [{array[begin..end]}]" + } + } + for begin in -1..=4 { + for end in -1..=3 { + echo "Values at {begin}..={end}: [{array[begin..=end]}]" + } + } +} + +main { + let data = ["apple", "banana", "cherry"] + test_range(data) +} diff --git a/src/tests/validity/array_get_range_local.ab b/src/tests/validity/array_get_range_local.ab new file mode 100644 index 00000000..973ef24b --- /dev/null +++ b/src/tests/validity/array_get_range_local.ab @@ -0,0 +1,125 @@ +// Output +// Values at -1..0: [] +// Values at -1..1: [apple] +// Values at -1..2: [apple banana] +// Values at -1..3: [apple banana cherry] +// Values at -1..4: [apple banana cherry] +// Values at 0..0: [] +// Values at 0..1: [apple] +// Values at 0..2: [apple banana] +// Values at 0..3: [apple banana cherry] +// Values at 0..4: [apple banana cherry] +// Values at 1..0: [] +// Values at 1..1: [] +// Values at 1..2: [banana] +// Values at 1..3: [banana cherry] +// Values at 1..4: [banana cherry] +// Values at 2..0: [] +// Values at 2..1: [] +// Values at 2..2: [] +// Values at 2..3: [cherry] +// Values at 2..4: [cherry] +// Values at 3..0: [] +// Values at 3..1: [] +// Values at 3..2: [] +// Values at 3..3: [] +// Values at 3..4: [] +// Values at 4..0: [] +// Values at 4..1: [] +// Values at 4..2: [] +// Values at 4..3: [] +// Values at 4..4: [] +// Values at -1..=-1: [] +// Values at -1..=0: [apple] +// Values at -1..=1: [apple banana] +// Values at -1..=2: [apple banana cherry] +// Values at -1..=3: [apple banana cherry] +// Values at 0..=-1: [] +// Values at 0..=0: [apple] +// Values at 0..=1: [apple banana] +// Values at 0..=2: [apple banana cherry] +// Values at 0..=3: [apple banana cherry] +// Values at 1..=-1: [] +// Values at 1..=0: [] +// Values at 1..=1: [banana] +// Values at 1..=2: [banana cherry] +// Values at 1..=3: [banana cherry] +// Values at 2..=-1: [] +// Values at 2..=0: [] +// Values at 2..=1: [] +// Values at 2..=2: [cherry] +// Values at 2..=3: [cherry] +// Values at 3..=-1: [] +// Values at 3..=0: [] +// Values at 3..=1: [] +// Values at 3..=2: [] +// Values at 3..=3: [] +// Values at 4..=-1: [] +// Values at 4..=0: [] +// Values at 4..=1: [] +// Values at 4..=2: [] +// Values at 4..=3: [] + +main { + let array = ["apple", "banana", "cherry"] + echo "Values at -1..0: [{array[-1..0]}]" + echo "Values at -1..1: [{array[-1..1]}]" + echo "Values at -1..2: [{array[-1..2]}]" + echo "Values at -1..3: [{array[-1..3]}]" + echo "Values at -1..4: [{array[-1..4]}]" + echo "Values at 0..0: [{array[0..0]}]" + echo "Values at 0..1: [{array[0..1]}]" + echo "Values at 0..2: [{array[0..2]}]" + echo "Values at 0..3: [{array[0..3]}]" + echo "Values at 0..4: [{array[0..4]}]" + echo "Values at 1..0: [{array[1..0]}]" + echo "Values at 1..1: [{array[1..1]}]" + echo "Values at 1..2: [{array[1..2]}]" + echo "Values at 1..3: [{array[1..3]}]" + echo "Values at 1..4: [{array[1..4]}]" + echo "Values at 2..0: [{array[2..0]}]" + echo "Values at 2..1: [{array[2..1]}]" + echo "Values at 2..2: [{array[2..2]}]" + echo "Values at 2..3: [{array[2..3]}]" + echo "Values at 2..4: [{array[2..4]}]" + echo "Values at 3..0: [{array[3..0]}]" + echo "Values at 3..1: [{array[3..1]}]" + echo "Values at 3..2: [{array[3..2]}]" + echo "Values at 3..3: [{array[3..3]}]" + echo "Values at 3..4: [{array[3..4]}]" + echo "Values at 4..0: [{array[4..0]}]" + echo "Values at 4..1: [{array[4..1]}]" + echo "Values at 4..2: [{array[4..2]}]" + echo "Values at 4..3: [{array[4..3]}]" + echo "Values at 4..4: [{array[4..4]}]" + echo "Values at -1..=-1: [{array[-1..=-1]}]" + echo "Values at -1..=0: [{array[-1..=0]}]" + echo "Values at -1..=1: [{array[-1..=1]}]" + echo "Values at -1..=2: [{array[-1..=2]}]" + echo "Values at -1..=3: [{array[-1..=3]}]" + echo "Values at 0..=-1: [{array[0..=-1]}]" + echo "Values at 0..=0: [{array[0..=0]}]" + echo "Values at 0..=1: [{array[0..=1]}]" + echo "Values at 0..=2: [{array[0..=2]}]" + echo "Values at 0..=3: [{array[0..=3]}]" + echo "Values at 1..=-1: [{array[1..=-1]}]" + echo "Values at 1..=0: [{array[1..=0]}]" + echo "Values at 1..=1: [{array[1..=1]}]" + echo "Values at 1..=2: [{array[1..=2]}]" + echo "Values at 1..=3: [{array[1..=3]}]" + echo "Values at 2..=-1: [{array[2..=-1]}]" + echo "Values at 2..=0: [{array[2..=0]}]" + echo "Values at 2..=1: [{array[2..=1]}]" + echo "Values at 2..=2: [{array[2..=2]}]" + echo "Values at 2..=3: [{array[2..=3]}]" + echo "Values at 3..=-1: [{array[3..=-1]}]" + echo "Values at 3..=0: [{array[3..=0]}]" + echo "Values at 3..=1: [{array[3..=1]}]" + echo "Values at 3..=2: [{array[3..=2]}]" + echo "Values at 3..=3: [{array[3..=3]}]" + echo "Values at 4..=-1: [{array[4..=-1]}]" + echo "Values at 4..=0: [{array[4..=0]}]" + echo "Values at 4..=1: [{array[4..=1]}]" + echo "Values at 4..=2: [{array[4..=2]}]" + echo "Values at 4..=3: [{array[4..=3]}]" +} From 98433afde7d18785e39eb58e96cd6aa6bd3fe28e Mon Sep 17 00:00:00 2001 From: Huw Walters Date: Sat, 30 Nov 2024 09:10:43 +0000 Subject: [PATCH 11/13] Rewrite "remove at" standard library function. --- src/std/array.ab | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/std/array.ab b/src/std/array.ab index e3b9d337..d5fb6957 100644 --- a/src/std/array.ab +++ b/src/std/array.ab @@ -40,23 +40,33 @@ pub fun last(array) { /// Removes an element at the index from the array pub fun remove_at(ref array: [], index: Num): Null { - trust $ unset {nameof array}[{index}] $ + let offset = index + 1 + let length = len(array) + array = array[0..index] + array[offset..length] } /// Removes an element at the index from the array, and returns it pub fun extract_at(ref array, index) { - let elem = array[index] - remove_at(array, index) - return elem + let element = array[index] + let offset = index + 1 + let length = len(array) + array = array[0..index] + array[offset..length] + return element } /// Removes the last element from the array, and returns it pub fun pop(ref array) { - let index = len(array) - 1 - return extract_at(array, index) + let length = len(array) + let index = length - 1 + let element = array[index] + array = array[0..index] + return element } /// Removes the first element from the array, and returns it pub fun shift(ref array) { - return extract_at(array, 0) + let length = len(array) + let element = array[0] + array = array[1..length] + return element } From a09f0b0e12dd9be06bf7b809880982d4474fcfc2 Mon Sep 17 00:00:00 2001 From: Huw Walters Date: Sun, 1 Dec 2024 11:05:08 +0000 Subject: [PATCH 12/13] Address Clippy warnings. --- src/modules/expression/binop/range.rs | 2 +- src/modules/function/invocation_utils.rs | 2 +- src/modules/variable/get.rs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/modules/expression/binop/range.rs b/src/modules/expression/binop/range.rs index f592656f..50fe997d 100644 --- a/src/modules/expression/binop/range.rs +++ b/src/modules/expression/binop/range.rs @@ -84,7 +84,7 @@ impl Range { if let Some(mut to) = self.to.get_integer_value() { // Make the upper bound exclusive. if !self.neq { - to = to + 1; + to += 1; } // Cap the lower bound at zero. let offset = max(from, 0); diff --git a/src/modules/function/invocation_utils.rs b/src/modules/function/invocation_utils.rs index 769ccce4..4edec5b5 100644 --- a/src/modules/function/invocation_utils.rs +++ b/src/modules/function/invocation_utils.rs @@ -67,7 +67,7 @@ fn run_function_with_args(meta: &mut ParserMetadata, mut fun: FunctionDecl, args })?; // Set the new return type or null if nothing was returned if let Type::Generic = fun.returns { - fun.returns = context.fun_ret_type.clone().unwrap_or_else(|| Type::Null); + fun.returns = context.fun_ret_type.clone().unwrap_or(Type::Null); }; // Set the new argument types fun.arg_types = args.to_vec(); diff --git a/src/modules/variable/get.rs b/src/modules/variable/get.rs index 9dcfbbaf..742d557f 100644 --- a/src/modules/variable/get.rs +++ b/src/modules/variable/get.rs @@ -76,6 +76,7 @@ impl SyntaxModule for VariableGet { } impl TranslateModule for VariableGet { + #[allow(clippy::collapsible_else_if)] fn translate(&self, meta: &mut TranslateMetadata) -> String { let name = self.get_translated_name(); // Text variables need to be encapsulated in string literals From e14373bd61afd564e77a6cf8ee988c1424e73830 Mon Sep 17 00:00:00 2001 From: Huw Walters Date: Sun, 1 Dec 2024 19:44:05 +0000 Subject: [PATCH 13/13] Assert array length as well as contents in tests. --- src/tests/stdlib/array_extract_at.ab | 22 +- src/tests/stdlib/array_first.ab | 14 +- src/tests/stdlib/array_last.ab | 14 +- src/tests/stdlib/array_pop.ab | 6 +- src/tests/stdlib/array_remove_at.ab | 22 +- src/tests/stdlib/array_search.ab | 16 +- src/tests/stdlib/array_shift.ab | 6 +- src/tests/validity/array_get_index_by_copy.ab | 4 +- src/tests/validity/array_get_index_by_ref.ab | 4 +- src/tests/validity/array_get_range_by_copy.ab | 128 ++++----- src/tests/validity/array_get_range_by_ref.ab | 128 ++++----- src/tests/validity/array_get_range_local.ab | 246 +++++++++--------- 12 files changed, 309 insertions(+), 301 deletions(-) diff --git a/src/tests/stdlib/array_extract_at.ab b/src/tests/stdlib/array_extract_at.ab index 642052c6..06c676d4 100644 --- a/src/tests/stdlib/array_extract_at.ab +++ b/src/tests/stdlib/array_extract_at.ab @@ -1,20 +1,20 @@ import { extract_at } from "std/array" // Output -// Value at -5: "", [apple banana cherry date] -// Value at -4: "apple", [apple banana cherry date] -// Value at -3: "banana", [apple banana cherry date] -// Value at -2: "cherry", [apple banana cherry date] -// Value at -1: "date", [apple banana cherry date] -// Value at 0: "apple", [banana cherry date] -// Value at 1: "banana", [apple cherry date] -// Value at 2: "cherry", [apple banana date] -// Value at 3: "date", [apple banana cherry] -// Value at 4: "", [apple banana cherry date] +// Value at -5: "" (4) [apple banana cherry date] +// Value at -4: "apple" (4) [apple banana cherry date] +// Value at -3: "banana" (4) [apple banana cherry date] +// Value at -2: "cherry" (4) [apple banana cherry date] +// Value at -1: "date" (4) [apple banana cherry date] +// Value at 0: "apple" (3) [banana cherry date] +// Value at 1: "banana" (3) [apple cherry date] +// Value at 2: "cherry" (3) [apple banana date] +// Value at 3: "date" (3) [apple banana cherry] +// Value at 4: "" (4) [apple banana cherry date] fun test_extract(inner: [Text], index: Num): Null { let value = extract_at(inner, index) - echo "Value at {index}: \"{value}\", [{inner}]" + echo "Value at {index}: \"{value}\" ({len(inner)}) [{inner}]" } main { diff --git a/src/tests/stdlib/array_first.ab b/src/tests/stdlib/array_first.ab index 59eb3863..2dcb58f7 100644 --- a/src/tests/stdlib/array_first.ab +++ b/src/tests/stdlib/array_first.ab @@ -1,17 +1,17 @@ import { first } from "std/array" // Output -// First of array: "apple", [apple banana cherry date] -// First of empty: "", [] +// First of data: "apple" (4) [apple banana cherry date] +// First of empty: "" (0) [] -fun test_first(label: Text, array: [Text]): Null { - let value = first(array) - echo "First of {label}: \"{value}\", [{array}]" +fun test_first(label: Text, inner: [Text]): Null { + let value = first(inner) + echo "First of {label}: \"{value}\" ({len(inner)}) [{inner}]" } main { - let array = ["apple", "banana", "cherry", "date"] + let data = ["apple", "banana", "cherry", "date"] let empty = [Text] - test_first("array", array) + test_first("data", data) test_first("empty", empty) } diff --git a/src/tests/stdlib/array_last.ab b/src/tests/stdlib/array_last.ab index 6e56401a..5d83ebf9 100644 --- a/src/tests/stdlib/array_last.ab +++ b/src/tests/stdlib/array_last.ab @@ -1,17 +1,17 @@ import { last } from "std/array" // Output -// Last of array: "date", [apple banana cherry date] -// Last of empty: "", [] +// Last of data: "date" (4) [apple banana cherry date] +// Last of empty: "" (0) [] -fun test_last(label: Text, array: [Text]): Null { - let value = last(array) - echo "Last of {label}: \"{value}\", [{array}]" +fun test_last(label: Text, inner: [Text]): Null { + let value = last(inner) + echo "Last of {label}: \"{value}\" ({len(inner)}) [{inner}]" } main { - let array = ["apple", "banana", "cherry", "date"] + let data = ["apple", "banana", "cherry", "date"] let empty = [Text] - test_last("array", array) + test_last("data", data) test_last("empty", empty) } diff --git a/src/tests/stdlib/array_pop.ab b/src/tests/stdlib/array_pop.ab index 91a3ee6a..8ab08123 100644 --- a/src/tests/stdlib/array_pop.ab +++ b/src/tests/stdlib/array_pop.ab @@ -1,12 +1,12 @@ import { pop } from "std/array" // Output -// Popped from data: "date", [apple banana cherry] -// Popped from empty: "", [] +// Popped from data: "date" (3) [apple banana cherry] +// Popped from empty: "" (0) [] fun test_pop(label: Text, inner: [Text]): Null { let value = pop(inner) - echo "Popped from {label}: \"{value}\", [{inner}]" + echo "Popped from {label}: \"{value}\" ({len(inner)}) [{inner}]" } main { diff --git a/src/tests/stdlib/array_remove_at.ab b/src/tests/stdlib/array_remove_at.ab index 9ae4a611..227f66db 100644 --- a/src/tests/stdlib/array_remove_at.ab +++ b/src/tests/stdlib/array_remove_at.ab @@ -1,20 +1,20 @@ import { remove_at } from "std/array" // Output -// Array after -5: [apple banana cherry date] -// Array after -4: [apple banana cherry date] -// Array after -3: [apple banana cherry date] -// Array after -2: [apple banana cherry date] -// Array after -1: [apple banana cherry date] -// Array after 0: [banana cherry date] -// Array after 1: [apple cherry date] -// Array after 2: [apple banana date] -// Array after 3: [apple banana cherry] -// Array after 4: [apple banana cherry date] +// Array after -5: (4) [apple banana cherry date] +// Array after -4: (4) [apple banana cherry date] +// Array after -3: (4) [apple banana cherry date] +// Array after -2: (4) [apple banana cherry date] +// Array after -1: (4) [apple banana cherry date] +// Array after 0: (3) [banana cherry date] +// Array after 1: (3) [apple cherry date] +// Array after 2: (3) [apple banana date] +// Array after 3: (3) [apple banana cherry] +// Array after 4: (4) [apple banana cherry date] fun test_remove(inner: [Text], index: Num): Null { let value = remove_at(inner, index) - echo "Array after {index}: [{inner}]" + echo "Array after {index}: ({len(inner)}) [{inner}]" } main { diff --git a/src/tests/stdlib/array_search.ab b/src/tests/stdlib/array_search.ab index bee14fd3..3bb4cc26 100644 --- a/src/tests/stdlib/array_search.ab +++ b/src/tests/stdlib/array_search.ab @@ -7,16 +7,16 @@ import { array_search } from "std/array" // Indices of "date": [3] // Indices of "kiwi": [] -fun test_search(array: [Text], value: Text): Null { - let indices = array_search(array, value) +fun test_search(inner: [Text], value: Text): Null { + let indices = array_search(inner, value) echo "Indices of \"{value}\": [{indices}]" } main { - let array = ["apple", "banana", "cherry", "date", "cherry", "banana", "apple"] - test_search(array, "apple") - test_search(array, "banana") - test_search(array, "cherry") - test_search(array, "date") - test_search(array, "kiwi") + let data = ["apple", "banana", "cherry", "date", "cherry", "banana", "apple"] + test_search(data, "apple") + test_search(data, "banana") + test_search(data, "cherry") + test_search(data, "date") + test_search(data, "kiwi") } diff --git a/src/tests/stdlib/array_shift.ab b/src/tests/stdlib/array_shift.ab index 60713578..ab4d31f5 100644 --- a/src/tests/stdlib/array_shift.ab +++ b/src/tests/stdlib/array_shift.ab @@ -1,12 +1,12 @@ import { shift } from "std/array" // Output -// Shifted from data: "apple", [banana cherry date] -// Shifted from empty: "", [] +// Shifted from data: "apple" (3) [banana cherry date] +// Shifted from empty: "" (0) [] fun test_shift(label: Text, inner: [Text]): Null { let value = shift(inner) - echo "Shifted from {label}: \"{value}\", [{inner}]" + echo "Shifted from {label}: \"{value}\" ({len(inner)}) [{inner}]" } main { diff --git a/src/tests/validity/array_get_index_by_copy.ab b/src/tests/validity/array_get_index_by_copy.ab index 422a5b27..4fe71b08 100644 --- a/src/tests/validity/array_get_index_by_copy.ab +++ b/src/tests/validity/array_get_index_by_copy.ab @@ -17,6 +17,6 @@ fun test_index(inner) { } main { - let array = ["apple", "banana", "cherry", "date"] - test_index(array) + let data = ["apple", "banana", "cherry", "date"] + test_index(data) } diff --git a/src/tests/validity/array_get_index_by_ref.ab b/src/tests/validity/array_get_index_by_ref.ab index 3130c078..7bfb3f2c 100644 --- a/src/tests/validity/array_get_index_by_ref.ab +++ b/src/tests/validity/array_get_index_by_ref.ab @@ -17,6 +17,6 @@ fun test_index(ref inner) { } main { - let array = ["apple", "banana", "cherry", "date"] - test_index(array) + let data = ["apple", "banana", "cherry", "date"] + test_index(data) } diff --git a/src/tests/validity/array_get_range_by_copy.ab b/src/tests/validity/array_get_range_by_copy.ab index 442ec579..5e59bfb4 100644 --- a/src/tests/validity/array_get_range_by_copy.ab +++ b/src/tests/validity/array_get_range_by_copy.ab @@ -1,74 +1,76 @@ // Output -// Values at -1..0: [] -// Values at -1..1: [apple] -// Values at -1..2: [apple banana] -// Values at -1..3: [apple banana cherry] -// Values at -1..4: [apple banana cherry] -// Values at 0..0: [] -// Values at 0..1: [apple] -// Values at 0..2: [apple banana] -// Values at 0..3: [apple banana cherry] -// Values at 0..4: [apple banana cherry] -// Values at 1..0: [] -// Values at 1..1: [] -// Values at 1..2: [banana] -// Values at 1..3: [banana cherry] -// Values at 1..4: [banana cherry] -// Values at 2..0: [] -// Values at 2..1: [] -// Values at 2..2: [] -// Values at 2..3: [cherry] -// Values at 2..4: [cherry] -// Values at 3..0: [] -// Values at 3..1: [] -// Values at 3..2: [] -// Values at 3..3: [] -// Values at 3..4: [] -// Values at 4..0: [] -// Values at 4..1: [] -// Values at 4..2: [] -// Values at 4..3: [] -// Values at 4..4: [] -// Values at -1..=-1: [] -// Values at -1..=0: [apple] -// Values at -1..=1: [apple banana] -// Values at -1..=2: [apple banana cherry] -// Values at -1..=3: [apple banana cherry] -// Values at 0..=-1: [] -// Values at 0..=0: [apple] -// Values at 0..=1: [apple banana] -// Values at 0..=2: [apple banana cherry] -// Values at 0..=3: [apple banana cherry] -// Values at 1..=-1: [] -// Values at 1..=0: [] -// Values at 1..=1: [banana] -// Values at 1..=2: [banana cherry] -// Values at 1..=3: [banana cherry] -// Values at 2..=-1: [] -// Values at 2..=0: [] -// Values at 2..=1: [] -// Values at 2..=2: [cherry] -// Values at 2..=3: [cherry] -// Values at 3..=-1: [] -// Values at 3..=0: [] -// Values at 3..=1: [] -// Values at 3..=2: [] -// Values at 3..=3: [] -// Values at 4..=-1: [] -// Values at 4..=0: [] -// Values at 4..=1: [] -// Values at 4..=2: [] -// Values at 4..=3: [] +// Values at -1..0: (0) [] +// Values at -1..1: (1) [apple] +// Values at -1..2: (2) [apple banana] +// Values at -1..3: (3) [apple banana cherry] +// Values at -1..4: (3) [apple banana cherry] +// Values at 0..0: (0) [] +// Values at 0..1: (1) [apple] +// Values at 0..2: (2) [apple banana] +// Values at 0..3: (3) [apple banana cherry] +// Values at 0..4: (3) [apple banana cherry] +// Values at 1..0: (0) [] +// Values at 1..1: (0) [] +// Values at 1..2: (1) [banana] +// Values at 1..3: (2) [banana cherry] +// Values at 1..4: (2) [banana cherry] +// Values at 2..0: (0) [] +// Values at 2..1: (0) [] +// Values at 2..2: (0) [] +// Values at 2..3: (1) [cherry] +// Values at 2..4: (1) [cherry] +// Values at 3..0: (0) [] +// Values at 3..1: (0) [] +// Values at 3..2: (0) [] +// Values at 3..3: (0) [] +// Values at 3..4: (0) [] +// Values at 4..0: (0) [] +// Values at 4..1: (0) [] +// Values at 4..2: (0) [] +// Values at 4..3: (0) [] +// Values at 4..4: (0) [] +// Values at -1..=-1: (0) [] +// Values at -1..=0: (1) [apple] +// Values at -1..=1: (2) [apple banana] +// Values at -1..=2: (3) [apple banana cherry] +// Values at -1..=3: (3) [apple banana cherry] +// Values at 0..=-1: (0) [] +// Values at 0..=0: (1) [apple] +// Values at 0..=1: (2) [apple banana] +// Values at 0..=2: (3) [apple banana cherry] +// Values at 0..=3: (3) [apple banana cherry] +// Values at 1..=-1: (0) [] +// Values at 1..=0: (0) [] +// Values at 1..=1: (1) [banana] +// Values at 1..=2: (2) [banana cherry] +// Values at 1..=3: (2) [banana cherry] +// Values at 2..=-1: (0) [] +// Values at 2..=0: (0) [] +// Values at 2..=1: (0) [] +// Values at 2..=2: (1) [cherry] +// Values at 2..=3: (1) [cherry] +// Values at 3..=-1: (0) [] +// Values at 3..=0: (0) [] +// Values at 3..=1: (0) [] +// Values at 3..=2: (0) [] +// Values at 3..=3: (0) [] +// Values at 4..=-1: (0) [] +// Values at 4..=0: (0) [] +// Values at 4..=1: (0) [] +// Values at 4..=2: (0) [] +// Values at 4..=3: (0) [] -fun test_range(array) { +fun test_range(inner) { for begin in -1..=4 { for end in 0..=4 { - echo "Values at {begin}..{end}: [{array[begin..end]}]" + let slice = inner[begin..end] + echo "Values at {begin}..{end}: ({len(slice)}) [{slice}]" } } for begin in -1..=4 { for end in -1..=3 { - echo "Values at {begin}..={end}: [{array[begin..=end]}]" + let slice = inner[begin..=end] + echo "Values at {begin}..={end}: ({len(slice)}) [{slice}]" } } } diff --git a/src/tests/validity/array_get_range_by_ref.ab b/src/tests/validity/array_get_range_by_ref.ab index 846a3170..1b5c1133 100644 --- a/src/tests/validity/array_get_range_by_ref.ab +++ b/src/tests/validity/array_get_range_by_ref.ab @@ -1,74 +1,76 @@ // Output -// Values at -1..0: [] -// Values at -1..1: [apple] -// Values at -1..2: [apple banana] -// Values at -1..3: [apple banana cherry] -// Values at -1..4: [apple banana cherry] -// Values at 0..0: [] -// Values at 0..1: [apple] -// Values at 0..2: [apple banana] -// Values at 0..3: [apple banana cherry] -// Values at 0..4: [apple banana cherry] -// Values at 1..0: [] -// Values at 1..1: [] -// Values at 1..2: [banana] -// Values at 1..3: [banana cherry] -// Values at 1..4: [banana cherry] -// Values at 2..0: [] -// Values at 2..1: [] -// Values at 2..2: [] -// Values at 2..3: [cherry] -// Values at 2..4: [cherry] -// Values at 3..0: [] -// Values at 3..1: [] -// Values at 3..2: [] -// Values at 3..3: [] -// Values at 3..4: [] -// Values at 4..0: [] -// Values at 4..1: [] -// Values at 4..2: [] -// Values at 4..3: [] -// Values at 4..4: [] -// Values at -1..=-1: [] -// Values at -1..=0: [apple] -// Values at -1..=1: [apple banana] -// Values at -1..=2: [apple banana cherry] -// Values at -1..=3: [apple banana cherry] -// Values at 0..=-1: [] -// Values at 0..=0: [apple] -// Values at 0..=1: [apple banana] -// Values at 0..=2: [apple banana cherry] -// Values at 0..=3: [apple banana cherry] -// Values at 1..=-1: [] -// Values at 1..=0: [] -// Values at 1..=1: [banana] -// Values at 1..=2: [banana cherry] -// Values at 1..=3: [banana cherry] -// Values at 2..=-1: [] -// Values at 2..=0: [] -// Values at 2..=1: [] -// Values at 2..=2: [cherry] -// Values at 2..=3: [cherry] -// Values at 3..=-1: [] -// Values at 3..=0: [] -// Values at 3..=1: [] -// Values at 3..=2: [] -// Values at 3..=3: [] -// Values at 4..=-1: [] -// Values at 4..=0: [] -// Values at 4..=1: [] -// Values at 4..=2: [] -// Values at 4..=3: [] +// Values at -1..0: (0) [] +// Values at -1..1: (1) [apple] +// Values at -1..2: (2) [apple banana] +// Values at -1..3: (3) [apple banana cherry] +// Values at -1..4: (3) [apple banana cherry] +// Values at 0..0: (0) [] +// Values at 0..1: (1) [apple] +// Values at 0..2: (2) [apple banana] +// Values at 0..3: (3) [apple banana cherry] +// Values at 0..4: (3) [apple banana cherry] +// Values at 1..0: (0) [] +// Values at 1..1: (0) [] +// Values at 1..2: (1) [banana] +// Values at 1..3: (2) [banana cherry] +// Values at 1..4: (2) [banana cherry] +// Values at 2..0: (0) [] +// Values at 2..1: (0) [] +// Values at 2..2: (0) [] +// Values at 2..3: (1) [cherry] +// Values at 2..4: (1) [cherry] +// Values at 3..0: (0) [] +// Values at 3..1: (0) [] +// Values at 3..2: (0) [] +// Values at 3..3: (0) [] +// Values at 3..4: (0) [] +// Values at 4..0: (0) [] +// Values at 4..1: (0) [] +// Values at 4..2: (0) [] +// Values at 4..3: (0) [] +// Values at 4..4: (0) [] +// Values at -1..=-1: (0) [] +// Values at -1..=0: (1) [apple] +// Values at -1..=1: (2) [apple banana] +// Values at -1..=2: (3) [apple banana cherry] +// Values at -1..=3: (3) [apple banana cherry] +// Values at 0..=-1: (0) [] +// Values at 0..=0: (1) [apple] +// Values at 0..=1: (2) [apple banana] +// Values at 0..=2: (3) [apple banana cherry] +// Values at 0..=3: (3) [apple banana cherry] +// Values at 1..=-1: (0) [] +// Values at 1..=0: (0) [] +// Values at 1..=1: (1) [banana] +// Values at 1..=2: (2) [banana cherry] +// Values at 1..=3: (2) [banana cherry] +// Values at 2..=-1: (0) [] +// Values at 2..=0: (0) [] +// Values at 2..=1: (0) [] +// Values at 2..=2: (1) [cherry] +// Values at 2..=3: (1) [cherry] +// Values at 3..=-1: (0) [] +// Values at 3..=0: (0) [] +// Values at 3..=1: (0) [] +// Values at 3..=2: (0) [] +// Values at 3..=3: (0) [] +// Values at 4..=-1: (0) [] +// Values at 4..=0: (0) [] +// Values at 4..=1: (0) [] +// Values at 4..=2: (0) [] +// Values at 4..=3: (0) [] -fun test_range(ref array) { +fun test_range(ref inner) { for begin in -1..=4 { for end in 0..=4 { - echo "Values at {begin}..{end}: [{array[begin..end]}]" + let slice = inner[begin..end] + echo "Values at {begin}..{end}: ({len(slice)}) [{slice}]" } } for begin in -1..=4 { for end in -1..=3 { - echo "Values at {begin}..={end}: [{array[begin..=end]}]" + let slice = inner[begin..=end] + echo "Values at {begin}..={end}: ({len(slice)}) [{slice}]" } } } diff --git a/src/tests/validity/array_get_range_local.ab b/src/tests/validity/array_get_range_local.ab index 973ef24b..f4746c52 100644 --- a/src/tests/validity/array_get_range_local.ab +++ b/src/tests/validity/array_get_range_local.ab @@ -1,125 +1,129 @@ // Output -// Values at -1..0: [] -// Values at -1..1: [apple] -// Values at -1..2: [apple banana] -// Values at -1..3: [apple banana cherry] -// Values at -1..4: [apple banana cherry] -// Values at 0..0: [] -// Values at 0..1: [apple] -// Values at 0..2: [apple banana] -// Values at 0..3: [apple banana cherry] -// Values at 0..4: [apple banana cherry] -// Values at 1..0: [] -// Values at 1..1: [] -// Values at 1..2: [banana] -// Values at 1..3: [banana cherry] -// Values at 1..4: [banana cherry] -// Values at 2..0: [] -// Values at 2..1: [] -// Values at 2..2: [] -// Values at 2..3: [cherry] -// Values at 2..4: [cherry] -// Values at 3..0: [] -// Values at 3..1: [] -// Values at 3..2: [] -// Values at 3..3: [] -// Values at 3..4: [] -// Values at 4..0: [] -// Values at 4..1: [] -// Values at 4..2: [] -// Values at 4..3: [] -// Values at 4..4: [] -// Values at -1..=-1: [] -// Values at -1..=0: [apple] -// Values at -1..=1: [apple banana] -// Values at -1..=2: [apple banana cherry] -// Values at -1..=3: [apple banana cherry] -// Values at 0..=-1: [] -// Values at 0..=0: [apple] -// Values at 0..=1: [apple banana] -// Values at 0..=2: [apple banana cherry] -// Values at 0..=3: [apple banana cherry] -// Values at 1..=-1: [] -// Values at 1..=0: [] -// Values at 1..=1: [banana] -// Values at 1..=2: [banana cherry] -// Values at 1..=3: [banana cherry] -// Values at 2..=-1: [] -// Values at 2..=0: [] -// Values at 2..=1: [] -// Values at 2..=2: [cherry] -// Values at 2..=3: [cherry] -// Values at 3..=-1: [] -// Values at 3..=0: [] -// Values at 3..=1: [] -// Values at 3..=2: [] -// Values at 3..=3: [] -// Values at 4..=-1: [] -// Values at 4..=0: [] -// Values at 4..=1: [] -// Values at 4..=2: [] -// Values at 4..=3: [] +// Values at -1..0: (0) [] +// Values at -1..1: (1) [apple] +// Values at -1..2: (2) [apple banana] +// Values at -1..3: (3) [apple banana cherry] +// Values at -1..4: (3) [apple banana cherry] +// Values at 0..0: (0) [] +// Values at 0..1: (1) [apple] +// Values at 0..2: (2) [apple banana] +// Values at 0..3: (3) [apple banana cherry] +// Values at 0..4: (3) [apple banana cherry] +// Values at 1..0: (0) [] +// Values at 1..1: (0) [] +// Values at 1..2: (1) [banana] +// Values at 1..3: (2) [banana cherry] +// Values at 1..4: (2) [banana cherry] +// Values at 2..0: (0) [] +// Values at 2..1: (0) [] +// Values at 2..2: (0) [] +// Values at 2..3: (1) [cherry] +// Values at 2..4: (1) [cherry] +// Values at 3..0: (0) [] +// Values at 3..1: (0) [] +// Values at 3..2: (0) [] +// Values at 3..3: (0) [] +// Values at 3..4: (0) [] +// Values at 4..0: (0) [] +// Values at 4..1: (0) [] +// Values at 4..2: (0) [] +// Values at 4..3: (0) [] +// Values at 4..4: (0) [] +// Values at -1..=-1: (0) [] +// Values at -1..=0: (1) [apple] +// Values at -1..=1: (2) [apple banana] +// Values at -1..=2: (3) [apple banana cherry] +// Values at -1..=3: (3) [apple banana cherry] +// Values at 0..=-1: (0) [] +// Values at 0..=0: (1) [apple] +// Values at 0..=1: (2) [apple banana] +// Values at 0..=2: (3) [apple banana cherry] +// Values at 0..=3: (3) [apple banana cherry] +// Values at 1..=-1: (0) [] +// Values at 1..=0: (0) [] +// Values at 1..=1: (1) [banana] +// Values at 1..=2: (2) [banana cherry] +// Values at 1..=3: (2) [banana cherry] +// Values at 2..=-1: (0) [] +// Values at 2..=0: (0) [] +// Values at 2..=1: (0) [] +// Values at 2..=2: (1) [cherry] +// Values at 2..=3: (1) [cherry] +// Values at 3..=-1: (0) [] +// Values at 3..=0: (0) [] +// Values at 3..=1: (0) [] +// Values at 3..=2: (0) [] +// Values at 3..=3: (0) [] +// Values at 4..=-1: (0) [] +// Values at 4..=0: (0) [] +// Values at 4..=1: (0) [] +// Values at 4..=2: (0) [] +// Values at 4..=3: (0) [] + +fun show_range(label, inner) { + echo "Values at {label}: ({len(inner)}) [{inner}]" +} main { - let array = ["apple", "banana", "cherry"] - echo "Values at -1..0: [{array[-1..0]}]" - echo "Values at -1..1: [{array[-1..1]}]" - echo "Values at -1..2: [{array[-1..2]}]" - echo "Values at -1..3: [{array[-1..3]}]" - echo "Values at -1..4: [{array[-1..4]}]" - echo "Values at 0..0: [{array[0..0]}]" - echo "Values at 0..1: [{array[0..1]}]" - echo "Values at 0..2: [{array[0..2]}]" - echo "Values at 0..3: [{array[0..3]}]" - echo "Values at 0..4: [{array[0..4]}]" - echo "Values at 1..0: [{array[1..0]}]" - echo "Values at 1..1: [{array[1..1]}]" - echo "Values at 1..2: [{array[1..2]}]" - echo "Values at 1..3: [{array[1..3]}]" - echo "Values at 1..4: [{array[1..4]}]" - echo "Values at 2..0: [{array[2..0]}]" - echo "Values at 2..1: [{array[2..1]}]" - echo "Values at 2..2: [{array[2..2]}]" - echo "Values at 2..3: [{array[2..3]}]" - echo "Values at 2..4: [{array[2..4]}]" - echo "Values at 3..0: [{array[3..0]}]" - echo "Values at 3..1: [{array[3..1]}]" - echo "Values at 3..2: [{array[3..2]}]" - echo "Values at 3..3: [{array[3..3]}]" - echo "Values at 3..4: [{array[3..4]}]" - echo "Values at 4..0: [{array[4..0]}]" - echo "Values at 4..1: [{array[4..1]}]" - echo "Values at 4..2: [{array[4..2]}]" - echo "Values at 4..3: [{array[4..3]}]" - echo "Values at 4..4: [{array[4..4]}]" - echo "Values at -1..=-1: [{array[-1..=-1]}]" - echo "Values at -1..=0: [{array[-1..=0]}]" - echo "Values at -1..=1: [{array[-1..=1]}]" - echo "Values at -1..=2: [{array[-1..=2]}]" - echo "Values at -1..=3: [{array[-1..=3]}]" - echo "Values at 0..=-1: [{array[0..=-1]}]" - echo "Values at 0..=0: [{array[0..=0]}]" - echo "Values at 0..=1: [{array[0..=1]}]" - echo "Values at 0..=2: [{array[0..=2]}]" - echo "Values at 0..=3: [{array[0..=3]}]" - echo "Values at 1..=-1: [{array[1..=-1]}]" - echo "Values at 1..=0: [{array[1..=0]}]" - echo "Values at 1..=1: [{array[1..=1]}]" - echo "Values at 1..=2: [{array[1..=2]}]" - echo "Values at 1..=3: [{array[1..=3]}]" - echo "Values at 2..=-1: [{array[2..=-1]}]" - echo "Values at 2..=0: [{array[2..=0]}]" - echo "Values at 2..=1: [{array[2..=1]}]" - echo "Values at 2..=2: [{array[2..=2]}]" - echo "Values at 2..=3: [{array[2..=3]}]" - echo "Values at 3..=-1: [{array[3..=-1]}]" - echo "Values at 3..=0: [{array[3..=0]}]" - echo "Values at 3..=1: [{array[3..=1]}]" - echo "Values at 3..=2: [{array[3..=2]}]" - echo "Values at 3..=3: [{array[3..=3]}]" - echo "Values at 4..=-1: [{array[4..=-1]}]" - echo "Values at 4..=0: [{array[4..=0]}]" - echo "Values at 4..=1: [{array[4..=1]}]" - echo "Values at 4..=2: [{array[4..=2]}]" - echo "Values at 4..=3: [{array[4..=3]}]" + let data = ["apple", "banana", "cherry"] + let slice = data[-1..0]; show_range("-1..0", slice) + let slice = data[-1..1]; show_range("-1..1", slice) + let slice = data[-1..2]; show_range("-1..2", slice) + let slice = data[-1..3]; show_range("-1..3", slice) + let slice = data[-1..4]; show_range("-1..4", slice) + let slice = data[0..0]; show_range("0..0", slice) + let slice = data[0..1]; show_range("0..1", slice) + let slice = data[0..2]; show_range("0..2", slice) + let slice = data[0..3]; show_range("0..3", slice) + let slice = data[0..4]; show_range("0..4", slice) + let slice = data[1..0]; show_range("1..0", slice) + let slice = data[1..1]; show_range("1..1", slice) + let slice = data[1..2]; show_range("1..2", slice) + let slice = data[1..3]; show_range("1..3", slice) + let slice = data[1..4]; show_range("1..4", slice) + let slice = data[2..0]; show_range("2..0", slice) + let slice = data[2..1]; show_range("2..1", slice) + let slice = data[2..2]; show_range("2..2", slice) + let slice = data[2..3]; show_range("2..3", slice) + let slice = data[2..4]; show_range("2..4", slice) + let slice = data[3..0]; show_range("3..0", slice) + let slice = data[3..1]; show_range("3..1", slice) + let slice = data[3..2]; show_range("3..2", slice) + let slice = data[3..3]; show_range("3..3", slice) + let slice = data[3..4]; show_range("3..4", slice) + let slice = data[4..0]; show_range("4..0", slice) + let slice = data[4..1]; show_range("4..1", slice) + let slice = data[4..2]; show_range("4..2", slice) + let slice = data[4..3]; show_range("4..3", slice) + let slice = data[4..4]; show_range("4..4", slice) + let slice = data[-1..=-1]; show_range("-1..=-1", slice) + let slice = data[-1..=0]; show_range("-1..=0", slice) + let slice = data[-1..=1]; show_range("-1..=1", slice) + let slice = data[-1..=2]; show_range("-1..=2", slice) + let slice = data[-1..=3]; show_range("-1..=3", slice) + let slice = data[0..=-1]; show_range("0..=-1", slice) + let slice = data[0..=0]; show_range("0..=0", slice) + let slice = data[0..=1]; show_range("0..=1", slice) + let slice = data[0..=2]; show_range("0..=2", slice) + let slice = data[0..=3]; show_range("0..=3", slice) + let slice = data[1..=-1]; show_range("1..=-1", slice) + let slice = data[1..=0]; show_range("1..=0", slice) + let slice = data[1..=1]; show_range("1..=1", slice) + let slice = data[1..=2]; show_range("1..=2", slice) + let slice = data[1..=3]; show_range("1..=3", slice) + let slice = data[2..=-1]; show_range("2..=-1", slice) + let slice = data[2..=0]; show_range("2..=0", slice) + let slice = data[2..=1]; show_range("2..=1", slice) + let slice = data[2..=2]; show_range("2..=2", slice) + let slice = data[2..=3]; show_range("2..=3", slice) + let slice = data[3..=-1]; show_range("3..=-1", slice) + let slice = data[3..=0]; show_range("3..=0", slice) + let slice = data[3..=1]; show_range("3..=1", slice) + let slice = data[3..=2]; show_range("3..=2", slice) + let slice = data[3..=3]; show_range("3..=3", slice) + let slice = data[4..=-1]; show_range("4..=-1", slice) + let slice = data[4..=0]; show_range("4..=0", slice) + let slice = data[4..=1]; show_range("4..=1", slice) + let slice = data[4..=2]; show_range("4..=2", slice) + let slice = data[4..=3]; show_range("4..=3", slice) }