Repository: syuilo/aiscript (ts)

Issue:
配列のmapなどのコールバック関数が受け取るインデックスが1から始まっている
配列の`map`, `filter`, `reduce`, `find`に渡すコールバック関数の引数でインデックスが受け取れるが、`0`ではなく`1`から始まっている。
バージョン1.12.0で配列のインデックスが`0`始まりに変更されているため、それに合わせた方がいい？
既存コードへの影響を考慮して維持するか別の関数にする？

例
```
[0 1 2].map(@(v i) {
  print(i) // 1 2 3
})
```

Pull request:
配列のmap系関数のコールバックが受け取るインデックスを0始まりに
<!-- ℹ お読みください
PRありがとうございます！ PRを作成する前に、以下をご確認ください:
- 可能であればタイトルに、以下で示すようなPRの種類が分かるキーワードをプリフィクスしてください。
  - fix / refactor / feat / enhance / perf / chore
  - また、PRの粒度が適切であることを確認してください。ひとつのPRに複数の種類の変更や関心を含めることは避けてください。
- このPRによって解決されるIssueがある場合は、そのIssueへの参照を本文内に含めてください。
- CHANGELOG.mdに変更点を追記してください。リファクタリングなど、利用者に影響を与えない変更についてはこの限りではありません。
- この変更により新たに作成、もしくは更新すべきドキュメントがないか確認してください。
- 機能追加やバグ修正をした場合は、可能であればテストケースを追加してください。
- テスト、Lintが通っていることを予め確認してください。
  - `npm run test`、`npm run lint`でぞれぞれ実施可能です
- `npm run api`を実行してAPIレポートを更新し、差分がある場合はコミットしてください。
ご協力ありがとうございます🤗
-->
<!-- ℹ README
Thank you for your PR! Before creating a PR, please check the following:
- If possible, prefix the title with a keyword that identifies the type of this PR, as shown below.
  - fix / refactor / feat / enhance / perf / chore
  - Also, make sure that the granularity of this PR is appropriate. Please do not include more than one type of change or interest in a single PR.
- If there is an Issue which will be resolved by this PR, please include a reference to the Issue in the text.
- Please add the summary of the changes to CHANGELOG.md. However, this is not necessary for changes that do not affect the users, such as refactoring.
- Check if there are any documents that need to be created or updated due to this change.
- If you have added a feature or fixed a bug, please add a test case if possible.
- Please make sure that tests and Lint are passed in advance.
  - You can run it with `npm run test` and `npm run lint`.
- Run `npm run api` to update the API report and commit it if there are any diffs.
Thanks for your cooperation 🤗
-->

# What
<!-- このPRで何をしたのか？ どう変わるのか？ -->
<!-- What did you do with this PR? How will it change things? -->
resolve #235

- 配列のmap系関数のコールバックが受け取るインデックスを0始まりに
- 対応するテストの追加
- CHANGELOG.mdの更新

# Why
<!-- なぜそうするのか？ どういう意図なのか？ 何が困っているのか？ -->
<!-- Why do you do it? What are your intentions? What is the problem? -->

# Additional info (optional)
<!-- テスト観点など -->
<!-- Test perspective, etc -->


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e7acee9..cff0c72 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,7 @@
 [Read translated version (en)](./translations/en/CHANGELOG.md)
 
+- 配列プロパティ`map`,`filter`,`reduce`,`find`に渡すコールバック関数が受け取るインデックスを0始まりに
+
 # 0.12.4
 - block comment `/* ... */`
 - Math:Infinity
diff --git a/src/interpreter/primitive-props.ts b/src/interpreter/primitive-props.ts
index 4d518cb..1c97d7c 100644
--- a/src/interpreter/primitive-props.ts
+++ b/src/interpreter/primitive-props.ts
@@ -112,7 +112,7 @@ export const PRIMITIVE_PROPS = {
 		map: (target: VArr): VFn => FN_NATIVE(async ([fn], opts) => {
 			assertFunction(fn);
 			const vals = target.value.map(async (item, i) => {
-				return await opts.call(fn, [item, NUM(i + 1)]);
+				return await opts.call(fn, [item, NUM(i)]);
 			});
 			return ARR(await Promise.all(vals));
 		}),
@@ -122,7 +122,7 @@ export const PRIMITIVE_PROPS = {
 			const vals = [] as Value[];
 			for (let i = 0; i < target.value.length; i++) {
 				const item = target.value[i]!;
-				const res = await opts.call(fn, [item, NUM(i + 1)]);
+				const res = await opts.call(fn, [item, NUM(i)]);
 				assertBoolean(res);
 				if (res.value) vals.push(item);
 			}
@@ -135,7 +135,7 @@ export const PRIMITIVE_PROPS = {
 			let accumulator = withInitialValue ? initialValue : target.value[0]!;
 			for (let i = withInitialValue ? 0 : 1; i < target.value.length; i++) {
 				const item = target.value[i]!;
-				accumulator = await opts.call(fn, [accumulator, item, NUM(i + 1)]);
+				accumulator = await opts.call(fn, [accumulator, item, NUM(i)]);
 			}
 			return accumulator;
 		}),
@@ -144,7 +144,7 @@ export const PRIMITIVE_PROPS = {
 			assertFunction(fn);
 			for (let i = 0; i < target.value.length; i++) {
 				const item = target.value[i]!;
-				const res = await opts.call(fn, [item, NUM(i + 1)]);
+				const res = await opts.call(fn, [item, NUM(i)]);
 				assertBoolean(res);
 				if (res.value) return item;
 			}


Test patch:
diff --git a/test/index.ts b/test/index.ts
index 4337c96..21c9866 100644
--- a/test/index.ts
+++ b/test/index.ts
@@ -232,7 +232,7 @@ describe('Comment', () => {
 		`);
 		eq(res, NUM(42));
 	});
-	
+
 	test.concurrent('multi line comment 2', async () => {
 		const res = await exe(`
 		/* variable declaration here...
@@ -991,7 +991,7 @@ describe('Function call', () => {
 		`);
 		eq(res, NUM(2));
 	});
-	
+
 	test.concurrent('std: throw AiScript error when required arg missing', async () => {
 		try {
 			await exe(`
@@ -1574,7 +1574,7 @@ describe('literal', () => {
 		const res = await exe('<: "ai saw a note \\"bebeyo\\"."');
 		eq(res, STR('ai saw a note "bebeyo".'));
 	});
-	
+
 	test.concurrent('Escaped single quote', async () => {
 		const res = await exe('<: \'ai saw a note \\\'bebeyo\\\'.\'');
 		eq(res, STR('ai saw a note \'bebeyo\'.'));
@@ -2242,6 +2242,15 @@ describe('primitive props', () => {
 			eq(res, ARR([NUM(2), NUM(4), NUM(6)]));
 		});
 
+		test.concurrent('map with index', async () =>
+		{
+			const res = await exe(`
+			let arr = [1, 2, 3]
+			<: arr.map(@(item, index) { item * index })
+			`);
+			eq(res, ARR([NUM(0), NUM(2), NUM(6)]));
+		});
+
 		test.concurrent('filter', async () => {
 			const res = await exe(`
 			let arr = [1, 2, 3]
@@ -2250,6 +2259,15 @@ describe('primitive props', () => {
 			eq(res, ARR([NUM(1), NUM(3)]));
 		});
 
+		test.concurrent('filter with index', async () =>
+		{
+			const res = await exe(`
+			let arr = [1, 2, 3, 4]
+			<: arr.filter(@(item, index) { item != 2 && index != 3 })
+			`);
+			eq(res, ARR([NUM(1), NUM(3)]));
+		});
+
 		test.concurrent('reduce', async () => {
 			const res = await exe(`
 			let arr = [1, 2, 3, 4]
@@ -2258,6 +2276,15 @@ describe('primitive props', () => {
 			eq(res, NUM(10));
 		});
 
+		test.concurrent('reduce with index', async () =>
+		{
+			const res = await exe(`
+			let arr = [1, 2, 3, 4]
+			<: arr.reduce(@(accumulator, currentValue, index) { (accumulator + (currentValue * index)) } 0)
+			`);
+			eq(res, NUM(20));
+		});
+
 		test.concurrent('find', async () => {
 			const res = await exe(`
 			let arr = ["abc", "def", "ghi"]
@@ -2266,6 +2293,15 @@ describe('primitive props', () => {
 			eq(res, STR('def'));
 		});
 
+		test.concurrent('find with index', async () =>
+		{
+			const res = await exe(`
+			let arr = ["abc1", "def1", "ghi1", "abc2", "def2", "ghi2"]
+			<: arr.find(@(item, index) { item.incl("e") && index > 1 })
+			`);
+			eq(res, STR('def2'));
+		});
+
 		test.concurrent('incl', async () => {
 			const res = await exe(`
 			let arr = ["abc", "def", "ghi"]