Repository: syuilo/aiscript
Pull request #283 (2023-07-04 22:05:15)

文字列がJson:parseでパース可能であるかを判定する関数
<!-- ℹ お読みください
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? -->
文字列が`Json:parse`でパース可能であるかを判定する関数`Json:parsable`を追加します。

# Why
<!-- なぜそうするのか？ どういう意図なのか？ 何が困っているのか？ -->
<!-- Why do you do it? What are your intentions? What is the problem? -->
solve #282

# Additional info (optional)
<!-- テスト観点など -->
<!-- Test perspective, etc -->


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/syuilo/aiscript/pull/283?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo) Report
Patch coverage: **`100.00`**% and project coverage change: **`+0.48`** :tada:
> Comparison is base [(`8ab9c27`)](https://app.codecov.io/gh/syuilo/aiscript/commit/8ab9c279009fe8453c9a65cf3bb7b359d081bb56?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo) 88.34% compared to head [(`a74b9b5`)](https://app.codecov.io/gh/syuilo/aiscript/pull/283?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo) 88.82%.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #283      +/-   ##
==========================================
+ Coverage   88.34%   88.82%   +0.48%     
==========================================
  Files          19       19              
  Lines        2916     2926      +10     
  Branches      485      497      +12     
==========================================
+ Hits         2576     2599      +23     
+ Misses        331      317      -14     
- Partials        9       10       +1     
```


| [Impacted Files](https://app.codecov.io/gh/syuilo/aiscript/pull/283?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo) | Coverage Δ | |
|---|---|---|
| [src/interpreter/lib/std.ts](https://app.codecov.io/gh/syuilo/aiscript/pull/283?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo#diff-c3JjL2ludGVycHJldGVyL2xpYi9zdGQudHM=) | `80.86% <100.00%> (+0.95%)` | :arrow_up: |

... and [1 file with indirect coverage changes](https://app.codecov.io/gh/syuilo/aiscript/pull/283/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo)


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/syuilo/aiscript/pull/283?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=syuilo).


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3150d1..3f5661d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
 [Read translated version (en)](./translations/en/CHANGELOG.md)
 
 - オブジェクトの値を配列化する`Obj:vals`を追加
+- 文字列が`Json:parse`でパース可能であるかを判定する関数`Json:parsable`を追加
 
 # 0.13.3
 - 乱数を生成するとき引数の最大値を戻り値に含むように
diff --git a/docs/std.md b/docs/std.md
index 7fa5d3f..ffa17fb 100644
--- a/docs/std.md
+++ b/docs/std.md
@@ -33,6 +33,9 @@ JSONを生成します。
 ### @Json:parse(_json_: str): value
 JSONをパースします。  
 
+### @Json:parsable(_str_: str): bool
+文字列がJSONとしてパース可能であるかの判定を行います。 
+
 ## :: Date
 
 ### @Date:now(): num
diff --git a/src/interpreter/lib/std.ts b/src/interpreter/lib/std.ts
index bec9de8..cfb70ac 100644
--- a/src/interpreter/lib/std.ts
+++ b/src/interpreter/lib/std.ts
@@ -148,6 +148,16 @@ export const std: Record<string, Value> = {
 		assertString(json);
 		return jsToVal(JSON.parse(json.value));
 	}),
+
+	'Json:parsable': FN_NATIVE(([str]) => {
+		assertString(str);
+		try {
+			JSON.parse(str.value);
+		} catch (e) {
+			return BOOL(false);
+		}
+		return BOOL(true);
+	}),
 	//#endregion
 
 	//#region Date


Test patch:
diff --git a/test/index.ts b/test/index.ts
index 0e4f2da..036a2e4 100644
--- a/test/index.ts
+++ b/test/index.ts
@@ -2550,6 +2550,37 @@ describe('std', () => {
 			`);
 			eq(res, STR('"<function>"'));
 		});
+
+		test.concurrent('parsable', async () => {
+			[
+				'',
+				'null',
+				'hoge',
+				'"hoge"',
+				'[',
+				'[]',
+				'{}'
+			].forEach(async (str)=>{
+				const res=await exe(`
+					<: Json:parsable('${str}')
+				`);
+				assert.deepEqual(res.type, 'bool');
+				if (res.value){
+					await exe(`
+						<: Json:parse('${str}')
+					`);
+				} else {
+					try {
+						await exe(`
+							<: Json:parse('${str}')
+						`);
+					} catch (e) {
+						if (e instanceof SyntaxError) return;
+					}
+					assert.fail()
+				}
+			}):
+		});
 	});
 });