arturo-lang / arturo

Simple, expressive & portable programming language for efficient scripting
http://arturo-lang.io
MIT License
703 stars 32 forks source link

[Core\Let] assign multiple values into one variable feature #1601

Closed RickBarretto closed 5 months ago

RickBarretto commented 5 months ago

Description

This PR adds the "unpack" feature that exists in other languages, like in Python [^1] or Ruby [^2].

At A Glance

[a [b] c]: [1 2 3 4 5]
a ; => 1
b ; => [2 3 4]
c ; => 5

How this is working:

$> [a [b] c]: [1 2 3 4 5]

$> a
=> 1

$> b
=> [2 3 4]

$> c
=> 5
$> [a [b] c]: [1 2 3]

$> a
=> 1

$> b
=> [2]

$> c
=> 3
$> [a [b] c]: [1 3]

══╡ VM Error ╞═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ <repl> ══

  Unsafe operation: You can't assign more values than available.
  not permitted in online playground
$> [a [b c] c]: [1 2 3]

══╡ VM Error ╞═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ <repl> ══

  Unsafe operation: Unpacking slice supports only one assignment
  not permitted in online playground
$> [a [b] c]: [1 2]

══╡ VM Error ╞═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════ <repl> ══

  Unsafe operation: You can't assign more values than available.
  not permitted in online playground

Type of change


[^1]: The * syntax "denotes iterable unpacking" https://docs.python.org/3/reference/expressions.html#expression-lists [^2]: The * is called "splat operator" https://docs.ruby-lang.org/en/2.0.0/syntax/calling_methods_rdoc.html#label-Array+to+Arguments+Conversion

RickBarretto commented 5 months ago

@drkameleon, I will need some help with the Errors, since I'm not used to our new syntax of printing errors, I used Error_OperationNotPermitted, but as you can see, that is not the best case for it because the message as made to be used for the Playground.

drkameleon commented 5 months ago

@drkameleon, I will need some help with the Errors, since I'm not used to our new syntax of printing errors, I used Error_OperationNotPermitted, but as you can see, that is not the best case for it because the message as made to be used for the Playground.

I have pulled the branch locally, compiled and tried it.

And the truth is it's working fine.

I would try catch the error in cases like this one too:

 [a [b] c [d]]: [1 2 3 4 5 6 7]

(which I did try for the sake of ... experimenting lol)

Now, regarding the error message themselves, I tried to suggest sth concrete and/or add something myself... but instead, I'll suggest something else:

RickBarretto commented 5 months ago

@drkameleon, I added the TODO, and also fixed some other things.

  1. For instance, I was accessing y.a, but without checking y type first, what is a horrible mistake and would expose a Segfault.
  2. The way I did, I hadn't managed a deprecation of multiple assignment to a single value, so I re-enabled this too.

Now, I'm getting this strange error. I mean, if we are throwing an exception, this shouldn't be able to run the code bellow it... So, WHAT?? LOL

image

Any ideas?

RickBarretto commented 5 months ago

image Ok, I tried to see where this is segfaulting, and... I don't really know. This isn't running the code bellow the raising (what is a good thing), but at the same time, this is segfaulting and I don't really know why or where...

drkameleon commented 5 months ago

Looks like it's going fine... 🎉

RickBarretto commented 5 months ago

Showcase:

import {unitt}!

suite "Allowed Operations" [

    test "multiple assignment to a single value when this is not a :block" [
        value: 5
        [a b c]: value

        assert -> a = value
        assert -> b = value
        assert -> c = value
    ]

    test "unpacked value always returns a :block" [
        value: [1]
        [[unpacker]]: value

        assert -> block? unpacker
        assert -> unpacker = value
    ]

    test "let can unpack values when the unpacker is on middle" [
        [a [b] c]: [1 2 3 4 5]

        assert -> a = 1
        assert -> b = [2 3 4]
        assert -> c = 5
    ]

    test "let can unpack values when the unpacker is on the left" [
        [[a] b c]: [1 2 3 4 5]

        assert -> a = [1 2 3]
        assert -> b = 4
        assert -> c = 5
    ]

    test "let can unpack values when the unpacker is on the right" [
        [a b [c]]: [1 2 3 4 5]

        assert -> a = 1
        assert -> b = 2
        assert -> c = [3 4 5]
    ]

    test "let can skip values to be unpacked if the unpacker is empty" [
        [a [] c]: [1 2 3 4 5]

        assert -> a = 1
        assert -> c = 5
    ]
]

suite "Unallowed Operations" [

    test "let can't unpack non- :block s" [
        assert -> throws? [
            value: 1
            [[unpacker]]: value
        ]
    ]

    test "let can't unpack more than once" [
        assert -> throws? [
            [[a] [b]]: [1 2 3 4]
        ]
    ]

    test "let can't assign to unknown types" [
        assert -> throws? [
            [*b]: [1 2 3 4]
        ]

        assert -> throws? [
            [a [*] c]: [1 2 3 4]
        ]
    ]

    test "let can't assign when values are missing" [
        assert -> throws? [
            [a b c]: [1 2]
        ]
    ]

    test "let can't assign when values are missing" [
        assert -> throws? [
            [a b c]: [1 2]
        ]
    ]

]

Tests are running ok locally, let's see and ready to merge! After this, I'll refactor this function...

RickBarretto commented 5 months ago

@drkameleon, ready to merge? 🚀