diff --git a/CHANGELOG.md b/CHANGELOG.md index fd30b69a..315a46c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - シンタックスエラーなどの位置情報を修正 - `arr.reduce`が空配列に対して初期値なしで呼び出された時、正式にエラーを出すよう - `str.pad_start`,`str.pad_end`を追加 +- `arr.insert`,`arr.remove`を追加 # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように diff --git a/docs/primitive-props.md b/docs/primitive-props.md index 8a51bece..bf9087eb 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -196,6 +196,18 @@ _fromIndex_ および _toIndex_ に関する挙動は`arr.slice`に準拠しま `arr.copy`同様シャローコピーであり、配列やオブジェクトの参照は維持されます。 _times_ には0以上の整数値を指定します。それ以外ではエラーになります。 +### @(_v_: arr).insert(_index_: num, _item_: value): null +**【この操作は配列を書き換えます】** +配列の _index_ の位置に _item_ を挿入します。\ +_index_ が負の場合は末尾から数えます。\ +_index_ が最後の要素より後の場合は末尾に追加します。 + +### @(_v_: arr).remove(_index_: num): value | null +**【この操作は配列を書き換えます】** +配列から _index_ の位置の要素を取り除き、その要素を返します。\ +_index_ が負の場合は末尾から数えます。\ +_index_ が最後の要素より後の場合は取り除かず、`null`を返します。 + ### @(_v_: arr).every(_func_: @(_item_: value, _index_: num) { bool }): bool 配列の全ての要素に対して _func_ が true を返す時のみ true 返します。空配列には常に true を返します。 diff --git a/src/interpreter/primitive-props.ts b/src/interpreter/primitive-props.ts index 9fcbb3ad..4818a087 100644 --- a/src/interpreter/primitive-props.ts +++ b/src/interpreter/primitive-props.ts @@ -316,6 +316,23 @@ const PRIMITIVE_PROPS: { } return FALSE; }), + + insert: (target: VArr): VFn => FN_NATIVE(async ([index, item], opts) => { + assertNumber(index); + expectAny(item); + + target.value.splice(index.value, 0, item); + + return NULL; + }), + + remove: (target: VArr): VFn => FN_NATIVE(async ([index], opts) => { + assertNumber(index); + + const removed = target.value.splice(index.value, 1); + + return removed[0] ?? NULL; + }), }, error: { diff --git a/test/index.ts b/test/index.ts index f67ccc99..0a6bddb7 100644 --- a/test/index.ts +++ b/test/index.ts @@ -2960,6 +2960,44 @@ describe('primitive props', () => { FALSE, ])); }); + + test.concurrent('insert', async () => { + const res = await exe(` + let arr1 = [0, 1, 2] + let res = [] + res.push(arr1.insert(3, 10)) // [0, 1, 2, 10] + res.push(arr1.insert(2, 20)) // [0, 1, 20, 2, 10] + res.push(arr1.insert(0, 30)) // [30, 0, 1, 20, 2, 10] + res.push(arr1.insert(-1, 40)) // [30, 0, 1, 20, 2, 40, 10] + res.push(arr1.insert(-4, 50)) // [30, 0, 1, 50, 20, 2, 40, 10] + res.push(arr1.insert(100, 60)) // [30, 0, 1, 50, 20, 2, 40, 10, 60] + res.push(arr1) + <: res + `); + eq(res, ARR([ + NULL, NULL, NULL, NULL, NULL, NULL, + ARR([NUM(30), NUM(0), NUM(1), NUM(50), NUM(20), NUM(2), NUM(40), NUM(10), NUM(60)]) + ])); + }); + + test.concurrent('remove', async () => { + const res = await exe(` + let arr1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + let res = [] + res.push(arr1.remove(9)) // 9 [0, 1, 2, 3, 4, 5, 6, 7, 8] + res.push(arr1.remove(3)) // 3 [0, 1, 2, 4, 5, 6, 7, 8] + res.push(arr1.remove(0)) // 0 [1, 2, 4, 5, 6, 7, 8] + res.push(arr1.remove(-1)) // 8 [1, 2, 4, 5, 6, 7] + res.push(arr1.remove(-5)) // 2 [1, 4, 5, 6, 7] + res.push(arr1.remove(100)) // null [1, 4, 5, 6, 7] + res.push(arr1) + <: res + `); + eq(res, ARR([ + NUM(9), NUM(3), NUM(0), NUM(8), NUM(2), NULL, + ARR([NUM(1), NUM(4), NUM(5), NUM(6), NUM(7)]) + ])); + }); }); });