Skip to content

Conversation

ArjixWasTaken
Copy link

@ArjixWasTaken ArjixWasTaken commented Apr 9, 2025

Closes #239

--

An initial implementation for array-destructuring.

I am not sure if my approach is "correct", I took inspiration from the AST of EcmaScript you may say.
EcmaScript has the concept of multiple variable definitions in one declaration (let a = 1, b = 1;).

Which inspired me to do the same (talking about the data structures), but for destructuring.
I am open to re-doing this from scratch in a different way if you want to.

--

This PR allows the following syntax:

let array = [1, 2, 3];
let [a, b] = array;

echo b;

which gets compiled to

#!/usr/bin/env bash
__AMBER_ARRAY_0=(1 2 3);
__0_array=("${__AMBER_ARRAY_0[@]}")
__ref_1i_i2_a_b=("${__0_array[@]}");
__1_a=${__ref_1i_i2_a_b[0]};
__2_b=${__ref_1i_i2_a_b[1]};

echo "${__2_b[@]}"

--

implementation details

  • VariableInit now holds a list of VariableDef
  • TranslateModule:
    • Added a temporary variable, named after all the definitions combined, to hold a reference to the array
    • Each definition is declared as a separate variable, and it uses the array reference to initialize

--

PS: I am not very proficient in bash, so I do not claim that this is a good implementation.

@ArjixWasTaken
Copy link
Author

I also tried to not modify any extra files, so no refactor was needed.
All the tests using cargo test pass.

@ArjixWasTaken ArjixWasTaken marked this pull request as draft April 9, 2025 20:07
@ArjixWasTaken
Copy link
Author

Made it a draft cause obviously I haven't added any tests for my changes yet.

syntax(meta, &mut *self.expr)?;

if is_destructured && !self.expr.get_type().is_array() {
panic!("Expected array type for destructured variable");
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

obviously a panic is bad, I am not yet familiar with the existing codebase, so this is temporary

@b1ek
Copy link
Member

b1ek commented Apr 10, 2025

this likely won't work because we can't check the array length at compile time, to ensure that there is enough items in the array:

let array = [1, 2, 3];
if (some_dynamic_condition) {
    array = [1];
}
let [a, b] = array;

i myself have experimented with this kind of features (namely #466, #432), and none of them worked because our compiler is very dumb meaning it doesn't understand how code works

@Ph0enixKM
Copy link
Member

@b1ek It's not a problem to make our compiler "smarter". We just have to store additional information in ctx of how long arrays are declared or if we ever know how long they can be when we assign an array to a variable.

@Ph0enixKM
Copy link
Member

@ArjixWasTaken before we code review this PR, please check if the problems that @b1ek mentioned can be resolved somehow (and how). I like this idea for a feature and would like to see it working correctly in Amber.

@ArjixWasTaken
Copy link
Author

ArjixWasTaken commented Apr 15, 2025

@Ph0enixKM

@ArjixWasTaken before we code review this PR, please check if the problems that @b1ek mentioned can be resolved somehow (and how). I like this idea for a feature and would like to see it working correctly in Amber.

After some thought, I think the best course of action is to raise a warning if we cannot determine the size of the array at build time.

And the user would be able to hide the warning by using a compiler flag.

That way

const [a, b] = [1, 2];

would be safe

but

const [a, b] = split("1,2", ",");

would be unsafe

@Ph0enixKM

This comment was marked as outdated.

@ArjixWasTaken
Copy link
Author

is there a reason to do it like that?
from my personal tests, bash works just fine even if you access an index out of range

unless there is a special bash mode that throws an error instead?

@Ph0enixKM
Copy link
Member

unless there is a special bash mode that throws an error instead?

Not too sure. I assumed that it would. I'd have to test it first on my computer when I get back home.

Scratch the idea I have written above. It wouldn't work with the current type system. It would need to assign each variable with Failable type since it doesn't know if it's Null / Num / Text or what

@Ph0enixKM
Copy link
Member

Ph0enixKM commented Apr 16, 2025

@ArjixWasTaken if we want to support dynamic sized arrays, then we can check if the array length matches destructed array and fails if not. Like:

const [a, b] = get_random_array() failed {
  echo "Variables couldn't be destructed from this array since it's length is less then the amount of the variables"
}

But this introduces inconsistency with the case when we know how many elements array has:

const [a, b] = ["a", "b"] // No `failed` here!

@Ph0enixKM
Copy link
Member

Ph0enixKM commented Apr 16, 2025

Now assuming that we want to implement this syntax version:

const [a, b] = get_random_array() failed {
  echo "Variables couldn't be destructed from this array since it's length is less then the amount of the variables"
}

Does this failed apply to the function or the array extraction? That's a thing that we should consider when attempting to go this way

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature] Array Destructuring
3 participants