-
Notifications
You must be signed in to change notification settings - Fork 90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement basic std functions to manipulate arrays #626
Implement basic std functions to manipulate arrays #626
Conversation
Test fails right now |
Yes I know, still working on it. |
@@ -32,6 +32,7 @@ wildmatch = "2.4.0" | |||
[dev-dependencies] | |||
assert_cmd = "2.0.14" | |||
predicates = "3.1.0" | |||
pretty_assertions = "1.4.1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creates more readable output for assert_eq!
macro. See https://crates.io/crates/pretty_assertions for more information.
if self.neq { | ||
let to_neq = if let Some(ExprType::Number(_)) = &self.to.value { | ||
to.parse::<isize>().unwrap_or_default().sub(1).to_string() | ||
let to = if let Some(to) = self.to.get_integer_value() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use compile-time arithmetic to convert Amber [1..11]
to Bash $(seq 1 10)
; fall back to calling bc
if not supplied a number.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's ok to assume we're given an integer not a decimal here, but we can always come back to this when we have a separate integer type.
|
||
impl Range { | ||
pub fn get_array_index(&self, meta: &mut TranslateMetadata) -> (String, String) { | ||
if let Some(from) = self.from.get_integer_value() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use compile-time arithmetic to convert Amber array[10..=12]
to Bash ${array[@]:10:3}
; fall back to calling bc
if not supplied a number.
@@ -101,6 +101,14 @@ impl Typed for Expr { | |||
} | |||
|
|||
impl Expr { | |||
pub fn get_integer_value(&self) -> Option<isize> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is intended to return an integer for compile-time arithmetic, but only when given integer literals like 42
or -42
. We're not worrying about float literals like 123.45
because this will only be called in contexts where an integer is required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was tempted to put this functionality into the TranslateModule
trait, but held off because it's fairly niche at the moment. However, I think doing compile time arithmetic where possible cannot be a bad idea; and we may decide to refactor if it becomes more widely used.
use crate::docs::module::DocumentationModule; | ||
use crate::error_type_match; | ||
use crate::modules::expression::expr::Expr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should avoid nested use
statements like the one I'm replacing here, as they're hard to read and maintain. RustRover has handy tools to "flatten use statements" and "optimize imports".
fn allow_index_accessor(index: &Expr, range: bool) -> bool { | ||
match (&index.kind, &index.value) { | ||
(Type::Num, _) => true, | ||
(Type::Array(_), Some(ExprType::Range(_))) => range, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Allow range indexing for echo array[2..5]
but not array[2..5] = [100, 101]
.
@@ -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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed this standard library function to more closely align with array_search
. I think it's ok to do this while we're still in alpha, but further down the line we may want to have the compiler automatically use deprecated function names (with a warning) via a new annotation:
#[replaces_deprecated(array_first_index)]
pub fun array_index(array, value): Num {
return result >= 0 | ||
} | ||
|
||
/// Returns the first element in the array | ||
pub fun first(array) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also call these array_first()
and array_last()
while we're at it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The major argument for more verbose standard library function names (e.g. containing at least one underscore) is that we're unlikely to want to reuse them for builtins (a case in which a #[replaces_deprecated]
annotation would not help). Note that I have already renamed lines
as split_lines
to make way for the lines
builtin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this in mind, we may want a separate discussion on renaming the wider set of standard library functions, before we release 0.4.0. I will create a discussion channel for this on Discord.
@@ -0,0 +1,25 @@ | |||
import { remove_at } from "std/array" | |||
|
|||
// Output |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Together with array_extract()
, this unit test is currently failing. I have boiled down the code under test to the simplest possible version, which I will post on Discord, along with a plea for help.
// Array after 4: (4) [apple banana cherry date] | ||
|
||
fun test_remove(inner: [Text], index: Num): Null { | ||
let value = remove_at(inner, index) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copy/paste error. Note to self: remove let value =
as value
is unused here.
let id = meta.gen_value_id(); | ||
let name = format!("__AMBER_ARRAY_ADD_{id}"); | ||
meta.stmt_queue.push_back(format!("{name}=({left} {right})")); | ||
format!("{quote}${{{name}[@]}}{quote}") | ||
format!("{quote}{dollar}{{{name}[@]}}{quote}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fixes the failing unit tests. We were passing the contents of the temporary array, rather than the name, in an eval
context; and the contents were all getting smooshed together into a single array item.
Please don't combine multiple issues into a single PR. This makes it nearly impossible to review, as there's too much context. The standard library changes should be in their own PR (as they were), while your range changes should be in another PR (which one should be merged first is up to you). Adding new dependencies which are not required for the work at hand should be in their own PR. And even more, stylistic changes of unrelated code ("optimize imports") should be their own PR. I'm sorry, but I'll have to close this PR. Please decouple your changes as much as possible, and open separate PRs. |
Fair point. Since I needed the compiler changes to implement the stdlib functions, I put them in the same branch. But I don't mind separating them; it's just cherry picking. |
The core changes should be merged first, but I'm not sure I agree that import optimisation needs its own PR, unless we touch the entire codebase (which I'm not suggesting btw). |
Implements some standard library functions that allow to manipulate arrays:
first(array)
to get the first element (merely a shorthand forarray[0]
)last(array)
to get the last element (merely a shorthand forarray[len(array) - 1]
)remove_at(array, index)
to remove an element at the index, translates tounset array[index]
extract_at(array, index)
— same asremove_at
, but also returns the elementpop(array)
— remove and return the last elementshift(array)
— remove and return the first element