From b444057ab8d70de5c510ee5e40fe69f84b431f7f Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Sat, 30 Mar 2024 17:26:36 +0000 Subject: [PATCH 01/47] Str:encode_uri --- docs/std.md | 4 ++++ src/interpreter/lib/std.ts | 5 +++++ test/index.ts | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/docs/std.md b/docs/std.md index 4e888add..99fe7365 100644 --- a/docs/std.md +++ b/docs/std.md @@ -131,6 +131,10 @@ _codePoints_の各要素は 0 以上、10FFFF16 以下である必要 UTF-8のバイト列を表す数値の配列から文字を生成します。 _bytes_の各要素は 0 以上、255 以下である必要があります。 +### #Str:encode_uri(v: str): str +v をURIとしてエンコードした文字列を返します。ただし以下の文字はエンコードされません。 +`A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; , / ? : @ & = + $ #` + ## :: Arr ### @Arr:create(_length_: num, _initial_?: value): arr 長さが`length`の配列を作成します。 diff --git a/src/interpreter/lib/std.ts b/src/interpreter/lib/std.ts index 469cba07..2aa7af86 100644 --- a/src/interpreter/lib/std.ts +++ b/src/interpreter/lib/std.ts @@ -498,6 +498,11 @@ export const std: Record = { return a.value; })))); }), + + 'Str:encode_uri': FN_NATIVE(([v]) => { + assertString(v); + return STR(encodeURI(v.value)); + }), //#endregion //#region Arr diff --git a/test/index.ts b/test/index.ts index 3c62c840..cb32cf10 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3057,6 +3057,13 @@ describe('std', () => { `); eq(res, NULL); }); + + test.concurrent('encode_uri', async () => { + const res = await exe(` + <: Str:encode_uri("https://example.com/?q=あいちゃん") + `); + eq(res, STR('https://example.com/?q=%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93')); + }); }); describe('Error', () => { From d4a5cc77708cb9a09383260e62bd90acba0070cd Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Sat, 30 Mar 2024 17:28:51 +0000 Subject: [PATCH 02/47] Str:encode_uri_component --- docs/std.md | 4 ++++ src/interpreter/lib/std.ts | 5 +++++ test/index.ts | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/docs/std.md b/docs/std.md index 99fe7365..db1d8514 100644 --- a/docs/std.md +++ b/docs/std.md @@ -135,6 +135,10 @@ _bytes_の各要素は 0 以上、255 以下である必要があります。 v をURIとしてエンコードした文字列を返します。ただし以下の文字はエンコードされません。 `A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; , / ? : @ & = + $ #` +### #Str:encode_uri_component(v: str): str +v をURIとしてエンコードした文字列を返します。ただし以下の文字はエンコードされません。 +`A-Z a-z 0-9 - _ . ! ~ * ' ( )` + ## :: Arr ### @Arr:create(_length_: num, _initial_?: value): arr 長さが`length`の配列を作成します。 diff --git a/src/interpreter/lib/std.ts b/src/interpreter/lib/std.ts index 2aa7af86..a41eb7da 100644 --- a/src/interpreter/lib/std.ts +++ b/src/interpreter/lib/std.ts @@ -502,6 +502,11 @@ export const std: Record = { 'Str:encode_uri': FN_NATIVE(([v]) => { assertString(v); return STR(encodeURI(v.value)); + }), + + 'Str:encode_uri_component': FN_NATIVE(([v]) => { + assertString(v); + return STR(encodeURIComponent(v.value)); }), //#endregion diff --git a/test/index.ts b/test/index.ts index cb32cf10..d53e13e1 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3064,6 +3064,13 @@ describe('std', () => { `); eq(res, STR('https://example.com/?q=%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93')); }); + + test.concurrent('encode_uri_component', async () => { + const res = await exe(` + <: Str:encode_uri_component("https://example.com/?q=あいちゃん") + `); + eq(res, STR('https%3A%2F%2Fexample.com%2F%3Fq%3D%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93')); + }); }); describe('Error', () => { From 0cfbaf7db26895588912ae2bc213f53e424a700e Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Sat, 30 Mar 2024 17:29:47 +0000 Subject: [PATCH 03/47] Str:decode_uri --- docs/std.md | 4 ++++ src/interpreter/lib/std.ts | 5 +++++ test/index.ts | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/docs/std.md b/docs/std.md index db1d8514..35343f18 100644 --- a/docs/std.md +++ b/docs/std.md @@ -139,6 +139,10 @@ v をURIとしてエンコードした文字列を返します。ただし以下 v をURIとしてエンコードした文字列を返します。ただし以下の文字はエンコードされません。 `A-Z a-z 0-9 - _ . ! ~ * ' ( )` +### #Str:decode_uri(v: str): str +v をURIとしてエスケープシーケンスをデコードした文字列を返します。 +ただし`Str:encode_uri`でエンコードされないエスケープシーケンスはデコードされません。 + ## :: Arr ### @Arr:create(_length_: num, _initial_?: value): arr 長さが`length`の配列を作成します。 diff --git a/src/interpreter/lib/std.ts b/src/interpreter/lib/std.ts index a41eb7da..71439105 100644 --- a/src/interpreter/lib/std.ts +++ b/src/interpreter/lib/std.ts @@ -507,6 +507,11 @@ export const std: Record = { 'Str:encode_uri_component': FN_NATIVE(([v]) => { assertString(v); return STR(encodeURIComponent(v.value)); + }), + + 'Str:decode_uri': FN_NATIVE(([v]) => { + assertString(v); + return STR(decodeURI(v.value)); }), //#endregion diff --git a/test/index.ts b/test/index.ts index d53e13e1..07e0a149 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3071,6 +3071,13 @@ describe('std', () => { `); eq(res, STR('https%3A%2F%2Fexample.com%2F%3Fq%3D%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93')); }); + + test.concurrent('decode_uri', async () => { + const res = await exe(` + <: Str:decode_uri("https%3A%2F%2Fexample.com%2F%3Fq%3D%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93") + `); + eq(res, STR('https%3A%2F%2Fexample.com%2F%3Fq%3Dあいちゃん')); + }); }); describe('Error', () => { From bbecdf65707301910fdd29addba43c454b8574be Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Sat, 30 Mar 2024 17:30:50 +0000 Subject: [PATCH 04/47] Str:decode_uri_component --- docs/std.md | 3 +++ src/interpreter/lib/std.ts | 5 +++++ test/index.ts | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/docs/std.md b/docs/std.md index 35343f18..a8fccf07 100644 --- a/docs/std.md +++ b/docs/std.md @@ -143,6 +143,9 @@ v をURIとしてエンコードした文字列を返します。ただし以下 v をURIとしてエスケープシーケンスをデコードした文字列を返します。 ただし`Str:encode_uri`でエンコードされないエスケープシーケンスはデコードされません。 +### #Str:decode_uri_component(v: str): str +v をURIとしてエスケープシーケンスをデコードした文字列を返します。 + ## :: Arr ### @Arr:create(_length_: num, _initial_?: value): arr 長さが`length`の配列を作成します。 diff --git a/src/interpreter/lib/std.ts b/src/interpreter/lib/std.ts index 71439105..e7d530cd 100644 --- a/src/interpreter/lib/std.ts +++ b/src/interpreter/lib/std.ts @@ -513,6 +513,11 @@ export const std: Record = { assertString(v); return STR(decodeURI(v.value)); }), + + 'Str:decode_uri_component': FN_NATIVE(([v]) => { + assertString(v); + return STR(decodeURIComponent(v.value)); + }), //#endregion //#region Arr diff --git a/test/index.ts b/test/index.ts index 07e0a149..fc1b5805 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3078,6 +3078,13 @@ describe('std', () => { `); eq(res, STR('https%3A%2F%2Fexample.com%2F%3Fq%3Dあいちゃん')); }); + + test.concurrent('decode_uri_component', async () => { + const res = await exe(` + <: Str:decode_uri_component("https%3A%2F%2Fexample.com%2F%3Fq%3D%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93") + `); + eq(res, STR('https://example.com/?q=あいちゃん')); + }); }); describe('Error', () => { From d599f8f3ce7dcafbf449eda42186aec7f8f9b2fb Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Sat, 30 Mar 2024 17:32:40 +0000 Subject: [PATCH 05/47] Update CHANGELOG.md Str:encode_uri Str:encode_uri_component Str:decode_uri Str:decode_uri_component --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a54dd98..3987e087 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - `arr.incl`の引数の型制限を廃止 - `Date:millisecond`を追加 - `arr.fill`, `arr.repeat`, `Arr:create`を追加 +- `Str:encode_uri`, `Str:encode_uri_component`, `Str:decode_uri`, `Str:decode_uri_component`を追加 # 0.17.0 - `package.json`を修正 From 1d094fd3838daf7f1accbf3d0abb5233d76b16b8 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Sat, 30 Mar 2024 17:38:12 +0000 Subject: [PATCH 06/47] =?UTF-8?q?doc=E3=81=AE=E6=9B=B8=E5=BC=8F=E3=81=8C?= =?UTF-8?q?=E9=96=93=E9=81=95=E3=81=A3=E3=81=A6=E3=81=84=E3=82=8B=E3=81=AE?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/std.md | 18 +++++++++--------- translations/en/docs/std.md | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/std.md b/docs/std.md index a8fccf07..fc4f9a91 100644 --- a/docs/std.md +++ b/docs/std.md @@ -110,40 +110,40 @@ _date_ を渡した場合、_date_に対応するミリ秒、 型: `str` 改行コード(LF)です。 -### #Str:lt(a: str, b: str): num +### @Str:lt(a: str, b: str): num a < b ならば -1、a == b ならば 0、a > b ならば 1 を返します。 arr.sortの比較関数として使用できます。 -### #Str:gt(a: str, b: str): num +### @Str:gt(a: str, b: str): num a > b ならば -1、a == b ならば 0、a < b ならば 1 を返します。 arr.sortの比較関数として使用できます。 -### #Str:from_codepoint(codepoint: num): str +### @Str:from_codepoint(codepoint: num): str Unicodeのコードポイントから文字を生成します。 _codepoint_ は 0 以上、10FFFF16 以下である必要があります。 -### #Str:from_unicode_codepoints(_codePoints_: `arr`): str +### @Str:from_unicode_codepoints(_codePoints_: `arr`): str Unicodeのコードポイント列を表す数値の配列から文字を生成します。 _codePoints_の各要素は 0 以上、10FFFF16 以下である必要があります。 -### #Str:from_utf8_bytes(_bytes_: `arr`): str +### @Str:from_utf8_bytes(_bytes_: `arr`): str UTF-8のバイト列を表す数値の配列から文字を生成します。 _bytes_の各要素は 0 以上、255 以下である必要があります。 -### #Str:encode_uri(v: str): str +### @Str:encode_uri(v: str): str v をURIとしてエンコードした文字列を返します。ただし以下の文字はエンコードされません。 `A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; , / ? : @ & = + $ #` -### #Str:encode_uri_component(v: str): str +### @Str:encode_uri_component(v: str): str v をURIとしてエンコードした文字列を返します。ただし以下の文字はエンコードされません。 `A-Z a-z 0-9 - _ . ! ~ * ' ( )` -### #Str:decode_uri(v: str): str +### @Str:decode_uri(v: str): str v をURIとしてエスケープシーケンスをデコードした文字列を返します。 ただし`Str:encode_uri`でエンコードされないエスケープシーケンスはデコードされません。 -### #Str:decode_uri_component(v: str): str +### @Str:decode_uri_component(v: str): str v をURIとしてエスケープシーケンスをデコードした文字列を返します。 ## :: Arr diff --git a/translations/en/docs/std.md b/translations/en/docs/std.md index 67d36237..32098a9e 100644 --- a/translations/en/docs/std.md +++ b/translations/en/docs/std.md @@ -113,19 +113,19 @@ Generates a numeric value from a hexadecimal string. Type: `str`. Newline code (LF). -### #Str:lt(a: str, b: str): num +### @Str:lt(a: str, b: str): num Returns -1 if a < b, 0 if a == b, or 1 if a > b. Using this as a comparison function for `arr.sort`, the array is sorted in ascending lexicographic order. -### #Str:gt(a: str, b: str): num +### @Str:gt(a: str, b: str): num Returns -1 if a > b, 0 if a == b, or 1 if a < b. Using this as the comparison function for `arr.sort`, the array is sorted in descending lexicographic order. -### #Str:from_codepoint(codepoint: num): str +### @Str:from_codepoint(codepoint: num): str Generates character from unicode code point. _codepoint_ must be greater than or equal to 0 and less than or equal to 10FFFFFF16. Multiple arguments are not supported yet. From 7458adccd8e6adceeebd5305da7e2f5ae53f8c12 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Sun, 31 Mar 2024 09:32:13 +0000 Subject: [PATCH 07/47] =?UTF-8?q?encode=E7=B3=BB=E3=81=AEdoc=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/std.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/std.md b/docs/std.md index fc4f9a91..759ed24d 100644 --- a/docs/std.md +++ b/docs/std.md @@ -131,20 +131,20 @@ _codePoints_の各要素は 0 以上、10FFFF16 以下である必要 UTF-8のバイト列を表す数値の配列から文字を生成します。 _bytes_の各要素は 0 以上、255 以下である必要があります。 -### @Str:encode_uri(v: str): str -v をURIとしてエンコードした文字列を返します。ただし以下の文字はエンコードされません。 +### @Str:encode_uri(uri: str): str +uri をURIとしてエンコードした文字列を返します。以下の文字はエンコードされません。 `A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; , / ? : @ & = + $ #` -### @Str:encode_uri_component(v: str): str -v をURIとしてエンコードした文字列を返します。ただし以下の文字はエンコードされません。 +### @Str:encode_uri_component(text: str): str +text をURI構成要素としてエンコードした文字列を返します。以下の文字はエンコードされません。 `A-Z a-z 0-9 - _ . ! ~ * ' ( )` -### @Str:decode_uri(v: str): str -v をURIとしてエスケープシーケンスをデコードした文字列を返します。 -ただし`Str:encode_uri`でエンコードされないエスケープシーケンスはデコードされません。 +### @Str:decode_uri(encoded_uri: str): str +encoded_uri をエンコードされたURIとしてデコードした文字列を返します。 +`Str:encode_uri`でエンコードされないエスケープシーケンスはデコードされません。 -### @Str:decode_uri_component(v: str): str -v をURIとしてエスケープシーケンスをデコードした文字列を返します。 +### @Str:decode_uri_component(encoded_text: str): str +encoded_text をエンコードされたURI構成要素としてデコードした文字列を返します。 ## :: Arr ### @Arr:create(_length_: num, _initial_?: value): arr From ab686d4a472341ef4398593c79b97d1aa99a34f3 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Sun, 31 Mar 2024 10:48:02 +0000 Subject: [PATCH 08/47] =?UTF-8?q?encode=E7=B3=BB=E9=96=A2=E6=95=B0?= =?UTF-8?q?=E3=81=AE=E5=90=8D=E5=89=8D=E7=A9=BA=E9=96=93=E3=82=92Str:?= =?UTF-8?q?=E3=81=8B=E3=82=89Uri:=E3=81=AB=E7=A7=BB=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- docs/std.md | 11 ++++++----- src/interpreter/lib/std.ts | 12 +++++++----- test/index.ts | 10 ++++++---- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3987e087..fce03652 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ - `arr.incl`の引数の型制限を廃止 - `Date:millisecond`を追加 - `arr.fill`, `arr.repeat`, `Arr:create`を追加 -- `Str:encode_uri`, `Str:encode_uri_component`, `Str:decode_uri`, `Str:decode_uri_component`を追加 +- `Uri:encode_uri`, `Uri:encode_uri_component`, `Uri:decode_uri`, `Uri:decode_uri_component`を追加 # 0.17.0 - `package.json`を修正 diff --git a/docs/std.md b/docs/std.md index 759ed24d..88cb72c8 100644 --- a/docs/std.md +++ b/docs/std.md @@ -131,19 +131,20 @@ _codePoints_の各要素は 0 以上、10FFFF16 以下である必要 UTF-8のバイト列を表す数値の配列から文字を生成します。 _bytes_の各要素は 0 以上、255 以下である必要があります。 -### @Str:encode_uri(uri: str): str +## :: Uri +### @Uri:encode_uri(uri: str): str uri をURIとしてエンコードした文字列を返します。以下の文字はエンコードされません。 `A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; , / ? : @ & = + $ #` -### @Str:encode_uri_component(text: str): str +### @Uri:encode_uri_component(text: str): str text をURI構成要素としてエンコードした文字列を返します。以下の文字はエンコードされません。 `A-Z a-z 0-9 - _ . ! ~ * ' ( )` -### @Str:decode_uri(encoded_uri: str): str +### @Uri:decode_uri(encoded_uri: str): str encoded_uri をエンコードされたURIとしてデコードした文字列を返します。 -`Str:encode_uri`でエンコードされないエスケープシーケンスはデコードされません。 +`Uri:encode_uri`でエンコードされないエスケープシーケンスはデコードされません。 -### @Str:decode_uri_component(encoded_text: str): str +### @Uri:decode_uri_component(encoded_text: str): str encoded_text をエンコードされたURI構成要素としてデコードした文字列を返します。 ## :: Arr diff --git a/src/interpreter/lib/std.ts b/src/interpreter/lib/std.ts index e7d530cd..df46d6f5 100644 --- a/src/interpreter/lib/std.ts +++ b/src/interpreter/lib/std.ts @@ -498,23 +498,25 @@ export const std: Record = { return a.value; })))); }), - - 'Str:encode_uri': FN_NATIVE(([v]) => { + //#endregion + + //#region Uri + 'Uri:encode_uri': FN_NATIVE(([v]) => { assertString(v); return STR(encodeURI(v.value)); }), - 'Str:encode_uri_component': FN_NATIVE(([v]) => { + 'Uri:encode_uri_component': FN_NATIVE(([v]) => { assertString(v); return STR(encodeURIComponent(v.value)); }), - 'Str:decode_uri': FN_NATIVE(([v]) => { + 'Uri:decode_uri': FN_NATIVE(([v]) => { assertString(v); return STR(decodeURI(v.value)); }), - 'Str:decode_uri_component': FN_NATIVE(([v]) => { + 'Uri:decode_uri_component': FN_NATIVE(([v]) => { assertString(v); return STR(decodeURIComponent(v.value)); }), diff --git a/test/index.ts b/test/index.ts index fc1b5805..a6fb2888 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3057,31 +3057,33 @@ describe('std', () => { `); eq(res, NULL); }); + }); + describe('Uri', () => { test.concurrent('encode_uri', async () => { const res = await exe(` - <: Str:encode_uri("https://example.com/?q=あいちゃん") + <: Uri:encode_uri("https://example.com/?q=あいちゃん") `); eq(res, STR('https://example.com/?q=%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93')); }); test.concurrent('encode_uri_component', async () => { const res = await exe(` - <: Str:encode_uri_component("https://example.com/?q=あいちゃん") + <: Uri:encode_uri_component("https://example.com/?q=あいちゃん") `); eq(res, STR('https%3A%2F%2Fexample.com%2F%3Fq%3D%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93')); }); test.concurrent('decode_uri', async () => { const res = await exe(` - <: Str:decode_uri("https%3A%2F%2Fexample.com%2F%3Fq%3D%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93") + <: Uri:decode_uri("https%3A%2F%2Fexample.com%2F%3Fq%3D%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93") `); eq(res, STR('https%3A%2F%2Fexample.com%2F%3Fq%3Dあいちゃん')); }); test.concurrent('decode_uri_component', async () => { const res = await exe(` - <: Str:decode_uri_component("https%3A%2F%2Fexample.com%2F%3Fq%3D%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93") + <: Uri:decode_uri_component("https%3A%2F%2Fexample.com%2F%3Fq%3D%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93") `); eq(res, STR('https://example.com/?q=あいちゃん')); }); From 3deebcf6a2dfd9fd916ea642595a3d77ea10216d Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Sun, 31 Mar 2024 15:39:02 +0000 Subject: [PATCH 09/47] =?UTF-8?q?Uri:encode=E7=B3=BB=E3=81=AE=E9=96=A2?= =?UTF-8?q?=E6=95=B0=E5=90=8D=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- docs/std.md | 10 +++++----- src/interpreter/lib/std.ts | 8 ++++---- test/index.ts | 16 ++++++++-------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fce03652..6b8a2571 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ - `arr.incl`の引数の型制限を廃止 - `Date:millisecond`を追加 - `arr.fill`, `arr.repeat`, `Arr:create`を追加 -- `Uri:encode_uri`, `Uri:encode_uri_component`, `Uri:decode_uri`, `Uri:decode_uri_component`を追加 +- `Uri:encode_full`, `Uri:encode_component`, `Uri:decode_full`, `Uri:decode_component`を追加 # 0.17.0 - `package.json`を修正 diff --git a/docs/std.md b/docs/std.md index 88cb72c8..efad9a3a 100644 --- a/docs/std.md +++ b/docs/std.md @@ -132,19 +132,19 @@ UTF-8のバイト列を表す数値の配列から文字を生成します。 _bytes_の各要素は 0 以上、255 以下である必要があります。 ## :: Uri -### @Uri:encode_uri(uri: str): str +### @Uri:encode_full(uri: str): str uri をURIとしてエンコードした文字列を返します。以下の文字はエンコードされません。 `A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; , / ? : @ & = + $ #` -### @Uri:encode_uri_component(text: str): str +### @Uri:encode_component(text: str): str text をURI構成要素としてエンコードした文字列を返します。以下の文字はエンコードされません。 `A-Z a-z 0-9 - _ . ! ~ * ' ( )` -### @Uri:decode_uri(encoded_uri: str): str +### @Uri:decode_full(encoded_uri: str): str encoded_uri をエンコードされたURIとしてデコードした文字列を返します。 -`Uri:encode_uri`でエンコードされないエスケープシーケンスはデコードされません。 +`Uri:encode_full`でエンコードされないエスケープシーケンスはデコードされません。 -### @Uri:decode_uri_component(encoded_text: str): str +### @Uri:decode_component(encoded_text: str): str encoded_text をエンコードされたURI構成要素としてデコードした文字列を返します。 ## :: Arr diff --git a/src/interpreter/lib/std.ts b/src/interpreter/lib/std.ts index df46d6f5..81d72d42 100644 --- a/src/interpreter/lib/std.ts +++ b/src/interpreter/lib/std.ts @@ -501,22 +501,22 @@ export const std: Record = { //#endregion //#region Uri - 'Uri:encode_uri': FN_NATIVE(([v]) => { + 'Uri:encode_full': FN_NATIVE(([v]) => { assertString(v); return STR(encodeURI(v.value)); }), - 'Uri:encode_uri_component': FN_NATIVE(([v]) => { + 'Uri:encode_component': FN_NATIVE(([v]) => { assertString(v); return STR(encodeURIComponent(v.value)); }), - 'Uri:decode_uri': FN_NATIVE(([v]) => { + 'Uri:decode_full': FN_NATIVE(([v]) => { assertString(v); return STR(decodeURI(v.value)); }), - 'Uri:decode_uri_component': FN_NATIVE(([v]) => { + 'Uri:decode_component': FN_NATIVE(([v]) => { assertString(v); return STR(decodeURIComponent(v.value)); }), diff --git a/test/index.ts b/test/index.ts index a6fb2888..3a1befeb 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3060,30 +3060,30 @@ describe('std', () => { }); describe('Uri', () => { - test.concurrent('encode_uri', async () => { + test.concurrent('encode_full', async () => { const res = await exe(` - <: Uri:encode_uri("https://example.com/?q=あいちゃん") + <: Uri:encode_full("https://example.com/?q=あいちゃん") `); eq(res, STR('https://example.com/?q=%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93')); }); - test.concurrent('encode_uri_component', async () => { + test.concurrent('encode_component', async () => { const res = await exe(` - <: Uri:encode_uri_component("https://example.com/?q=あいちゃん") + <: Uri:encode_component("https://example.com/?q=あいちゃん") `); eq(res, STR('https%3A%2F%2Fexample.com%2F%3Fq%3D%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93')); }); - test.concurrent('decode_uri', async () => { + test.concurrent('decode_full', async () => { const res = await exe(` - <: Uri:decode_uri("https%3A%2F%2Fexample.com%2F%3Fq%3D%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93") + <: Uri:decode_full("https%3A%2F%2Fexample.com%2F%3Fq%3D%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93") `); eq(res, STR('https%3A%2F%2Fexample.com%2F%3Fq%3Dあいちゃん')); }); - test.concurrent('decode_uri_component', async () => { + test.concurrent('decode_component', async () => { const res = await exe(` - <: Uri:decode_uri_component("https%3A%2F%2Fexample.com%2F%3Fq%3D%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93") + <: Uri:decode_component("https%3A%2F%2Fexample.com%2F%3Fq%3D%E3%81%82%E3%81%84%E3%81%A1%E3%82%83%E3%82%93") `); eq(res, STR('https://example.com/?q=あいちゃん')); }); From f7face7911baad21e7391f95e50d8e40f9071359 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Sun, 31 Mar 2024 15:41:45 +0000 Subject: [PATCH 10/47] =?UTF-8?q?doc=E3=82=92=E4=BF=AE=E6=AD=A3(Uri:decode?= =?UTF-8?q?=5Ffull)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/std.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/std.md b/docs/std.md index efad9a3a..536e3112 100644 --- a/docs/std.md +++ b/docs/std.md @@ -142,7 +142,8 @@ text をURI構成要素としてエンコードした文字列を返します。 ### @Uri:decode_full(encoded_uri: str): str encoded_uri をエンコードされたURIとしてデコードした文字列を返します。 -`Uri:encode_full`でエンコードされないエスケープシーケンスはデコードされません。 +以下の文字に対応するエスケープシーケンスはデコードされません。 +`; , / ? : @ & = + $ #` ### @Uri:decode_component(encoded_text: str): str encoded_text をエンコードされたURI構成要素としてデコードした文字列を返します。 From 67f393e1e0a6d25a3c8612e2163af6224d61895d Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 11 Apr 2024 09:02:07 +0000 Subject: [PATCH 11/47] arr.splice --- CHANGELOG.md | 1 + docs/primitive-props.md | 8 +++++ src/interpreter/primitive-props.ts | 16 ++++++++++ test/index.ts | 48 ++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89464f88..5df721e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - `Date:millisecond`を追加 - `arr.fill`, `arr.repeat`, `Arr:create`を追加 - JavaScriptのように分割代入ができるように(現段階では機能は最小限) +- `arr.splice`を追加 # 0.17.0 - `package.json`を修正 diff --git a/docs/primitive-props.md b/docs/primitive-props.md index 0c9c2919..b0c98249 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -184,6 +184,14 @@ _fromIndex_ および _toIndex_ に関する挙動は`arr.slice`に準拠しま `arr.copy`同様シャローコピーであり、配列やオブジェクトの参照は維持されます。 _times_ には0以上の整数値を指定します。それ以外ではエラーになります。 +### @(_v_: arr).splice(_index_: num, _remove_count_: num, _items_: arr\): arr\ +**【この操作は配列を書き換えます】** +配列の _index_ から _remove_count_ 個の要素を取り除き、_items_ の要素を挿入します。 +返り値は取り除いた要素の配列を返します。\ +_index_ が負の場合、末尾から数えます。_index_ が最後の要素より後の場合、要素を取り除きません。\ +_remove_count_ を省略した場合、末尾まで取り除きます。\ +_items_ を省略した場合、何も挿入しません。 + ## エラー型 ### #(_v_: error).name 型: `str` diff --git a/src/interpreter/primitive-props.ts b/src/interpreter/primitive-props.ts index 911313ac..5d05794d 100644 --- a/src/interpreter/primitive-props.ts +++ b/src/interpreter/primitive-props.ts @@ -279,6 +279,22 @@ const PRIMITIVE_PROPS: { throw e; } }), + + splice: (target: VArr): VFn => FN_NATIVE(async ([idx, rc, vs], opts) => { + assertNumber(idx) + const index = (idx.value < -target.value.length) ? 0 + : (idx.value < 0) ? target.value.length + idx.value + : (idx.value >= target.value.length) ? target.value.length + : idx.value; + + const remove_count = (rc != null) ? (assertNumber(rc), rc.value) + : target.value.length - index; + + const items = (vs != null) ? (assertArray(vs), vs.value) : []; + + const result = target.value.splice(index, remove_count, ...items); + return ARR(result); + }), }, error: { diff --git a/test/index.ts b/test/index.ts index 5d4634e9..a2cdf9fa 100644 --- a/test/index.ts +++ b/test/index.ts @@ -2849,6 +2849,54 @@ describe('primitive props', () => { ARR([]), ])); }); + + test.concurrent('splice (full)', async () => { + const res = await exe(` + let arr1 = [0, 1, 2, 3] + let arr2 = arr1.splice(1, 2, [10]) + <: [arr1, arr2] + `); + eq(res, ARR([ + ARR([NUM(0), NUM(10), NUM(3)]), + ARR([NUM(1), NUM(2)]), + ])); + }); + + test.concurrent('splice (negative-index)', async () => { + const res = await exe(` + let arr1 = [0, 1, 2, 3] + let arr2 = arr1.splice(-1, 0, [10, 20]) + <: [arr1, arr2] + `); + eq(res, ARR([ + ARR([NUM(0), NUM(1), NUM(2), NUM(10), NUM(20), NUM(3)]), + ARR([]), + ])); + }); + + test.concurrent('splice (larger-index)', async () => { + const res = await exe(` + let arr1 = [0, 1, 2, 3] + let arr2 = arr1.splice(4, 100, [10, 20]) + <: [arr1, arr2] + `); + eq(res, ARR([ + ARR([NUM(0), NUM(1), NUM(2), NUM(3), NUM(10), NUM(20)]), + ARR([]), + ])); + }); + + test.concurrent('splice (single argument)', async () => { + const res = await exe(` + let arr1 = [0, 1, 2, 3] + let arr2 = arr1.splice(1) + <: [arr1, arr2] + `); + eq(res, ARR([ + ARR([NUM(0)]), + ARR([NUM(1), NUM(2), NUM(3)]), + ])); + }); }); }); From 3761bf1c895ca6fff7a98246991b0517ff12e383 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 11 Apr 2024 09:07:16 +0000 Subject: [PATCH 12/47] =?UTF-8?q?doc=E4=BF=AE=E6=AD=A3(arr.splice)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/primitive-props.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/primitive-props.md b/docs/primitive-props.md index b0c98249..f6c54bdb 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -186,9 +186,9 @@ _times_ には0以上の整数値を指定します。それ以外ではエラ ### @(_v_: arr).splice(_index_: num, _remove_count_: num, _items_: arr\): arr\ **【この操作は配列を書き換えます】** -配列の _index_ から _remove_count_ 個の要素を取り除き、_items_ の要素を挿入します。 -返り値は取り除いた要素の配列を返します。\ -_index_ が負の場合、末尾から数えます。_index_ が最後の要素より後の場合、要素を取り除きません。\ +配列の _index_ から _remove_count_ 個の要素を取り除き、その位置に _items_ の要素を挿入します。 +返り値として、取り除いた要素の配列を返します。\ +_index_ が負の場合は末尾から数えます。_index_ が最後の要素より後の場合は要素を取り除きません。\ _remove_count_ を省略した場合、末尾まで取り除きます。\ _items_ を省略した場合、何も挿入しません。 From 3bd6c25a2f508cfb082bb7916b339f43d6c6124e Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 11 Apr 2024 09:16:59 +0000 Subject: [PATCH 13/47] =?UTF-8?q?doc=E4=BF=AE=E6=AD=A3=20(arr.splice)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/primitive-props.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/primitive-props.md b/docs/primitive-props.md index f6c54bdb..345d2296 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -188,7 +188,8 @@ _times_ には0以上の整数値を指定します。それ以外ではエラ **【この操作は配列を書き換えます】** 配列の _index_ から _remove_count_ 個の要素を取り除き、その位置に _items_ の要素を挿入します。 返り値として、取り除いた要素の配列を返します。\ -_index_ が負の場合は末尾から数えます。_index_ が最後の要素より後の場合は要素を取り除きません。\ +_index_ が負の場合は末尾から数えます。\ +_index_ が最後の要素より後の場合は要素を取り除かず、挿入は末尾に追加します。\ _remove_count_ を省略した場合、末尾まで取り除きます。\ _items_ を省略した場合、何も挿入しません。 From a1e98cff0b7c0ef5d6ba536fd411103b46698fc0 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 11 Apr 2024 09:23:23 +0000 Subject: [PATCH 14/47] fix lint --- src/interpreter/primitive-props.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interpreter/primitive-props.ts b/src/interpreter/primitive-props.ts index 5d05794d..c8af6182 100644 --- a/src/interpreter/primitive-props.ts +++ b/src/interpreter/primitive-props.ts @@ -281,9 +281,9 @@ const PRIMITIVE_PROPS: { }), splice: (target: VArr): VFn => FN_NATIVE(async ([idx, rc, vs], opts) => { - assertNumber(idx) + assertNumber(idx); const index = (idx.value < -target.value.length) ? 0 - : (idx.value < 0) ? target.value.length + idx.value + : (idx.value < 0) ? target.value.length + idx.value : (idx.value >= target.value.length) ? target.value.length : idx.value; From 84437df8a794f317bcc7a59990329d3ce260b5da Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 11 Apr 2024 14:27:37 +0000 Subject: [PATCH 15/47] arr.flat --- CHANGELOG.md | 1 + docs/primitive-props.md | 4 ++++ src/interpreter/primitive-props.ts | 25 ++++++++++++++++++++++++- test/index.ts | 23 +++++++++++++++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89464f88..464836ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - `Date:millisecond`を追加 - `arr.fill`, `arr.repeat`, `Arr:create`を追加 - JavaScriptのように分割代入ができるように(現段階では機能は最小限) +- `arr.flat`を追加 # 0.17.0 - `package.json`を修正 diff --git a/docs/primitive-props.md b/docs/primitive-props.md index 0c9c2919..db3d73e4 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -184,6 +184,10 @@ _fromIndex_ および _toIndex_ に関する挙動は`arr.slice`に準拠しま `arr.copy`同様シャローコピーであり、配列やオブジェクトの参照は維持されます。 _times_ には0以上の整数値を指定します。それ以外ではエラーになります。 +### @(_v_: arr).flat(_depth_: num): arr +配列に含まれる配列を _depth_ で指定した深さの階層まで結合した新しい配列を作成します。 +_depth_ には0以上の整数値を指定します。省略時は1になります。 + ## エラー型 ### #(_v_: error).name 型: `str` diff --git a/src/interpreter/primitive-props.ts b/src/interpreter/primitive-props.ts index 911313ac..8a2cdb81 100644 --- a/src/interpreter/primitive-props.ts +++ b/src/interpreter/primitive-props.ts @@ -2,7 +2,7 @@ import { substring, length, indexOf, toArray } from 'stringz'; import { AiScriptRuntimeError } from '../error.js'; import { textEncoder } from '../const.js'; -import { assertArray, assertBoolean, assertFunction, assertNumber, assertString, expectAny, eq } from './util.js'; +import { assertArray, assertBoolean, assertFunction, assertNumber, assertString, expectAny, eq, isArray } from './util.js'; import { ARR, FALSE, FN_NATIVE, NULL, NUM, STR, TRUE } from './value.js'; import type { Value, VArr, VFn, VNum, VStr, VError } from './value.js'; @@ -279,6 +279,29 @@ const PRIMITIVE_PROPS: { throw e; } }), + + flat: (target: VArr): VFn => FN_NATIVE(async ([depth], opts) => { + depth = depth ?? NUM(1); + assertNumber(depth); + if (!Number.isInteger(depth.value)) throw new AiScriptRuntimeError('arr.flat expected integer, got non-integer'); + if (depth.value < 0) throw new AiScriptRuntimeError('arr.flat expected non-negative number, got negative'); + const flat = (arr: Value[], depth: number, result: Value[]) => { + if (depth === 0) { + result.push(...arr); + return; + } + for (const v of arr) { + if (isArray(v)) { + flat(v.value, depth - 1, result); + } else { + result.push(v); + } + } + }; + const result: Value[] = []; + flat(target.value, depth.value, result); + return ARR(result); + }), }, error: { diff --git a/test/index.ts b/test/index.ts index 5d4634e9..bb60464c 100644 --- a/test/index.ts +++ b/test/index.ts @@ -2849,6 +2849,29 @@ describe('primitive props', () => { ARR([]), ])); }); + + test.concurrent('flat', async () => { + const res = await exe(` + var arr1 = [0, [1], [2, 3], [4, [5, 6]]] + let arr2 = arr1.flat() + let arr3 = arr1.flat(2) + <: [arr1, arr2, arr3] + `); + eq(res, ARR([ + ARR([ + NUM(0), ARR([NUM(1)]), ARR([NUM(2), NUM(3)]), + ARR([NUM(4), ARR([NUM(5), NUM(6)])]) + ]), // target not changed + ARR([ + NUM(0), NUM(1), NUM(2), NUM(3), + NUM(4), ARR([NUM(5), NUM(6)]), + ]), + ARR([ + NUM(0), NUM(1), NUM(2), NUM(3), + NUM(4), NUM(5), NUM(6), + ]), + ])); + }); }); }); From 19d3d9f0372a9982df6145d3a5d160ab65d5fa55 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 11 Apr 2024 15:26:08 +0000 Subject: [PATCH 16/47] =?UTF-8?q?arr.flat=5Fmap=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- docs/primitive-props.md | 4 ++++ src/interpreter/primitive-props.ts | 10 ++++++++++ test/index.ts | 20 ++++++++++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 464836ad..61aac3b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ - `Date:millisecond`を追加 - `arr.fill`, `arr.repeat`, `Arr:create`を追加 - JavaScriptのように分割代入ができるように(現段階では機能は最小限) -- `arr.flat`を追加 +- `arr.flat`,`arr.flat_map`を追加 # 0.17.0 - `package.json`を修正 diff --git a/docs/primitive-props.md b/docs/primitive-props.md index db3d73e4..b993b1f0 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -188,6 +188,10 @@ _times_ には0以上の整数値を指定します。それ以外ではエラ 配列に含まれる配列を _depth_ で指定した深さの階層まで結合した新しい配列を作成します。 _depth_ には0以上の整数値を指定します。省略時は1になります。 +### @(_v_: arr).flat_map(_func_: @(_item_: value, _index_: num) { value }): arr +配列の各要素を _func_ の返り値で置き換えた後、1階層平坦化した新しい配列を作成します。 +_func_ は非同期的に呼び出されます。 + ## エラー型 ### #(_v_: error).name 型: `str` diff --git a/src/interpreter/primitive-props.ts b/src/interpreter/primitive-props.ts index 8a2cdb81..920d0853 100644 --- a/src/interpreter/primitive-props.ts +++ b/src/interpreter/primitive-props.ts @@ -302,6 +302,16 @@ const PRIMITIVE_PROPS: { flat(target.value, depth.value, result); return ARR(result); }), + + flat_map: (target: VArr): VFn => FN_NATIVE(async ([fn], opts) => { + assertFunction(fn); + const vals = target.value.map(async (item, i) => { + const result = await opts.call(fn, [item, NUM(i)]); + return isArray(result) ? result.value : result; + }); + const mapped_vals = await Promise.all(vals); + return ARR(mapped_vals.flat()); + }), }, error: { diff --git a/test/index.ts b/test/index.ts index bb60464c..962a34d9 100644 --- a/test/index.ts +++ b/test/index.ts @@ -2872,6 +2872,26 @@ describe('primitive props', () => { ]), ])); }); + + test.concurrent('flat_map', async () => { + const res = await exe(` + let arr1 = [0, 1, 2] + let arr2 = ["a", "b"] + let arr3 = arr1.flat_map(@(x){ arr2.map(@(y){ [x, y] }) }) + <: [arr1, arr3] + `); + eq(res, ARR([ + ARR([NUM(0), NUM(1), NUM(2)]), // target not changed + ARR([ + ARR([NUM(0), STR("a")]), + ARR([NUM(0), STR("b")]), + ARR([NUM(1), STR("a")]), + ARR([NUM(1), STR("b")]), + ARR([NUM(2), STR("a")]), + ARR([NUM(2), STR("b")]), + ]), + ])); + }); }); }); From de79a9540adc87d27123057337333a32d96ee205 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 11 Apr 2024 18:18:11 +0000 Subject: [PATCH 17/47] =?UTF-8?q?doc=E4=BF=AE=E6=AD=A3(arr.splice)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/primitive-props.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/primitive-props.md b/docs/primitive-props.md index 345d2296..a5a20b8d 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -184,7 +184,7 @@ _fromIndex_ および _toIndex_ に関する挙動は`arr.slice`に準拠しま `arr.copy`同様シャローコピーであり、配列やオブジェクトの参照は維持されます。 _times_ には0以上の整数値を指定します。それ以外ではエラーになります。 -### @(_v_: arr).splice(_index_: num, _remove_count_: num, _items_: arr\): arr\ +### @(_v_: arr).splice(_index_: num, _remove_count_?: num, _items_?: arr\): arr\ **【この操作は配列を書き換えます】** 配列の _index_ から _remove_count_ 個の要素を取り除き、その位置に _items_ の要素を挿入します。 返り値として、取り除いた要素の配列を返します。\ From 35d7e7ff3f6a67a0a1f6282179706e7c8907237b Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 11 Apr 2024 18:21:46 +0000 Subject: [PATCH 18/47] =?UTF-8?q?doc=E4=BF=AE=E6=AD=A3(arr.flat)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/primitive-props.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/primitive-props.md b/docs/primitive-props.md index b993b1f0..438160e9 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -184,7 +184,7 @@ _fromIndex_ および _toIndex_ に関する挙動は`arr.slice`に準拠しま `arr.copy`同様シャローコピーであり、配列やオブジェクトの参照は維持されます。 _times_ には0以上の整数値を指定します。それ以外ではエラーになります。 -### @(_v_: arr).flat(_depth_: num): arr +### @(_v_: arr).flat(_depth_?: num): arr 配列に含まれる配列を _depth_ で指定した深さの階層まで結合した新しい配列を作成します。 _depth_ には0以上の整数値を指定します。省略時は1になります。 From 491de8bfa7c0f97b464aa908018d04b0185845ab Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Tue, 23 Apr 2024 10:38:51 +0000 Subject: [PATCH 19/47] =?UTF-8?q?arr.sort=E3=81=A7=E8=A1=8C=E3=82=8F?= =?UTF-8?q?=E3=82=8C=E3=82=8B=E5=88=86=E5=89=B2=E3=81=95=E3=82=8C=E3=81=9F?= =?UTF-8?q?=E5=90=84=E3=82=BD=E3=83=BC=E3=83=88=E3=82=92=E9=9D=9E=E5=90=8C?= =?UTF-8?q?=E6=9C=9F=E5=87=A6=E7=90=86=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/interpreter/primitive-props.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/interpreter/primitive-props.ts b/src/interpreter/primitive-props.ts index 911313ac..9bc03ee5 100644 --- a/src/interpreter/primitive-props.ts +++ b/src/interpreter/primitive-props.ts @@ -231,8 +231,9 @@ const PRIMITIVE_PROPS: { const mergeSort = async (arr: Value[], comp: VFn): Promise => { if (arr.length <= 1) return arr; const mid = Math.floor(arr.length / 2); - const left = await mergeSort(arr.slice(0, mid), comp); - const right = await mergeSort(arr.slice(mid), comp); + const left_promise = mergeSort(arr.slice(0, mid), comp); + const right_promise = mergeSort(arr.slice(mid), comp); + const [left, right] = await Promise.all([left_promise, right_promise]); return merge(left, right, comp); }; const merge = async (left: Value[], right: Value[], comp: VFn): Promise => { From 2b1fd0ca9ed5c212ff0d2356e77bcfc27b9d9d4b Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Tue, 23 Apr 2024 10:39:15 +0000 Subject: [PATCH 20/47] update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89464f88..a99d0f5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - `Date:millisecond`を追加 - `arr.fill`, `arr.repeat`, `Arr:create`を追加 - JavaScriptのように分割代入ができるように(現段階では機能は最小限) +- `arr.sort`の処理を非同期的にして高速化 # 0.17.0 - `package.json`を修正 From 32a8d66505553282089a8fbfe48c79cdd7ca04fb Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 25 Apr 2024 16:43:26 +0000 Subject: [PATCH 21/47] fix CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d04db96d..6bd27575 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ [Read translated version (en)](./translations/en/CHANGELOG.md) # 未リリース分 +- `arr.sort`の処理を非同期的にして高速化 # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように @@ -14,7 +15,6 @@ - ネストされた名前空間下の変数を参照できるように - `arr.every`, `arr.some`を追加 - `Date:to_iso_str`を追加 -- `arr.sort`の処理を非同期的にして高速化 # 0.17.0 - `package.json`を修正 From c7a5a046b66a58169b4626e9850ea09061e53e25 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 25 Apr 2024 16:50:00 +0000 Subject: [PATCH 22/47] fix indent --- src/interpreter/primitive-props.ts | 2 +- test/index.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/interpreter/primitive-props.ts b/src/interpreter/primitive-props.ts index 5f3a38d6..524eb2ac 100644 --- a/src/interpreter/primitive-props.ts +++ b/src/interpreter/primitive-props.ts @@ -294,7 +294,7 @@ const PRIMITIVE_PROPS: { const result = target.value.splice(index, remove_count, ...items); return ARR(result); - }), + }), every: (target: VArr): VFn => FN_NATIVE(async ([fn], opts) => { assertFunction(fn); diff --git a/test/index.ts b/test/index.ts index db7f3cd6..eb9e3310 100644 --- a/test/index.ts +++ b/test/index.ts @@ -2922,10 +2922,10 @@ describe('primitive props', () => { eq(res, ARR([ ARR([NUM(0)]), ARR([NUM(1), NUM(2), NUM(3)]), - ])); - }); + ])); + }); - test.concurrent('every', async () => { + test.concurrent('every', async () => { const res = await exe(` let arr1 = [0, 1, 2, 3] let res1 = arr1.every(@(v,i){v==0 || i > 0}) From 492f6b81695d6391b244c11677e0d8fedffda49a Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 25 Apr 2024 16:52:00 +0000 Subject: [PATCH 23/47] fix indent --- test/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/index.ts b/test/index.ts index eb9e3310..d951dfae 100644 --- a/test/index.ts +++ b/test/index.ts @@ -2925,7 +2925,7 @@ describe('primitive props', () => { ])); }); - test.concurrent('every', async () => { + test.concurrent('every', async () => { const res = await exe(` let arr1 = [0, 1, 2, 3] let res1 = arr1.every(@(v,i){v==0 || i > 0}) From a402ab44094f14d16640120be55b19e7e25d14ad Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 25 Apr 2024 16:55:29 +0000 Subject: [PATCH 24/47] fix CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e88625cd..d0f1b4b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ [Read translated version (en)](./translations/en/CHANGELOG.md) # 未リリース分 +- `arr.flat`,`arr.flat_map`を追加 # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように @@ -14,7 +15,6 @@ - ネストされた名前空間下の変数を参照できるように - `arr.every`, `arr.some`を追加 - `Date:to_iso_str`を追加 -- `arr.flat`,`arr.flat_map`を追加 # 0.17.0 - `package.json`を修正 From c48c3ed27daf9d362420b5e714dad72a84354266 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 25 Apr 2024 16:59:24 +0000 Subject: [PATCH 25/47] fix CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a654335e..b52626fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ [Read translated version (en)](./translations/en/CHANGELOG.md) # 未リリース分 +- `arr.splice`を追加 # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように @@ -14,7 +15,6 @@ - ネストされた名前空間下の変数を参照できるように - `arr.every`, `arr.some`を追加 - `Date:to_iso_str`を追加 -- `arr.splice`を追加 # 0.17.0 - `package.json`を修正 From c982c39aacb0e359bb581ee4286fa43eb3648b68 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Apr 2024 19:13:03 +0000 Subject: [PATCH 26/47] Bump actions/checkout from 4.1.3 to 4.1.4 in the gh-actions group Bumps the gh-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 4.1.3 to 4.1.4 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4.1.3...v4.1.4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gh-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/api.yml | 2 +- .github/workflows/gh-pages.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/publish-nightly-dev.yml | 2 +- .github/workflows/publish-nightly-next.yml | 2 +- .github/workflows/test.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index e2e714e0..338ccf10 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.1.3 + uses: actions/checkout@v4.1.4 - name: Setup Node.js uses: actions/setup-node@v4.0.2 diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 00064d0f..622154b9 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.1.3 + uses: actions/checkout@v4.1.4 - name: Setup Node.js uses: actions/setup-node@v4.0.2 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ea4397af..a659fbbf 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.1.3 + uses: actions/checkout@v4.1.4 - name: Setup Node.js uses: actions/setup-node@v4.0.2 diff --git a/.github/workflows/publish-nightly-dev.yml b/.github/workflows/publish-nightly-dev.yml index 7402073d..688a7485 100644 --- a/.github/workflows/publish-nightly-dev.yml +++ b/.github/workflows/publish-nightly-dev.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.1.3 + uses: actions/checkout@v4.1.4 with: ref: ${{ env.BRANCH }} diff --git a/.github/workflows/publish-nightly-next.yml b/.github/workflows/publish-nightly-next.yml index 224fb9ae..f041b9e9 100644 --- a/.github/workflows/publish-nightly-next.yml +++ b/.github/workflows/publish-nightly-next.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.1.3 + uses: actions/checkout@v4.1.4 with: ref: ${{ env.BRANCH }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f4c95cc0..3f2eb121 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.1.3 + uses: actions/checkout@v4.1.4 - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4.0.2 From ef711609ec283f5b20b2027787038445ed2ce541 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Fri, 3 May 2024 07:40:18 +0000 Subject: [PATCH 27/47] add test Date: - Date:year - Date:month - Date:day - Date:hour - Date:minute - Date:second - Date:millisecond --- test/index.ts | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/test/index.ts b/test/index.ts index 290efa73..29873c4c 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3187,6 +3187,55 @@ describe('std', () => { }); describe('Date', () => { + test.concurrent('year', async () => { + const res = await exe(` + <: [Date:year(0), Date:year(1714946889010)] + `); + eq(res.value, [NUM(1970), NUM(2024)]); + }); + + test.concurrent('month', async () => { + const res = await exe(` + <: [Date:month(0), Date:month(1714946889010)] + `); + eq(res.value, [NUM(1), NUM(5)]); + }); + + test.concurrent('day', async () => { + const res = await exe(` + <: [Date:day(0), Date:day(1714946889010)] + `); + eq(res.value, [NUM(1), NUM(6)]); + }); + + test.concurrent('hour', async () => { + const res = await exe(` + <: [Date:hour(0), Date:hour(1714946889010)] + `); + eq(res.value, [NUM(0), NUM(7)]); + }); + + test.concurrent('minute', async () => { + const res = await exe(` + <: [Date:minute(0), Date:minute(1714946889010)] + `); + eq(res.value, [NUM(0), NUM(8)]); + }); + + test.concurrent('second', async () => { + const res = await exe(` + <: [Date:second(0), Date:second(1714946889010)] + `); + eq(res.value, [NUM(0), NUM(9)]); + }); + + test.concurrent('millisecond', async () => { + const res = await exe(` + <: [Date:millisecond(0), Date:millisecond(1714946889010)] + `); + eq(res.value, [NUM(0), NUM(10)]); + }); + test.concurrent('to_iso_str', async () => { const res = await exe(` let d1 = Date:parse("2024-04-12T01:47:46.021+09:00") From 769764a995e69d42b59bf24c89e2f316e741ad84 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Fri, 3 May 2024 07:40:50 +0000 Subject: [PATCH 28/47] replace || -> ?? --- src/interpreter/lib/std.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/interpreter/lib/std.ts b/src/interpreter/lib/std.ts index 33daec7d..b3d5081d 100644 --- a/src/interpreter/lib/std.ts +++ b/src/interpreter/lib/std.ts @@ -183,37 +183,37 @@ export const std: Record = { 'Date:year': FN_NATIVE(([v]) => { if (v) { assertNumber(v); } - return NUM(new Date(v?.value || Date.now()).getFullYear()); + return NUM(new Date(v?.value ?? Date.now()).getFullYear()); }), 'Date:month': FN_NATIVE(([v]) => { if (v) { assertNumber(v); } - return NUM(new Date(v?.value || Date.now()).getMonth() + 1); + return NUM(new Date(v?.value ?? Date.now()).getMonth() + 1); }), 'Date:day': FN_NATIVE(([v]) => { if (v) { assertNumber(v); } - return NUM(new Date(v?.value || Date.now()).getDate()); + return NUM(new Date(v?.value ?? Date.now()).getDate()); }), 'Date:hour': FN_NATIVE(([v]) => { if (v) { assertNumber(v); } - return NUM(new Date(v?.value || Date.now()).getHours()); + return NUM(new Date(v?.value ?? Date.now()).getHours()); }), 'Date:minute': FN_NATIVE(([v]) => { if (v) { assertNumber(v); } - return NUM(new Date(v?.value || Date.now()).getMinutes()); + return NUM(new Date(v?.value ?? Date.now()).getMinutes()); }), 'Date:second': FN_NATIVE(([v]) => { if (v) { assertNumber(v); } - return NUM(new Date(v?.value || Date.now()).getSeconds()); + return NUM(new Date(v?.value ?? Date.now()).getSeconds()); }), 'Date:millisecond': FN_NATIVE(([v]) => { if (v) { assertNumber(v); } - return NUM(new Date(v?.value || Date.now()).getMilliseconds()); + return NUM(new Date(v?.value ?? Date.now()).getMilliseconds()); }), 'Date:parse': FN_NATIVE(([v]) => { From 19225bbd9a39b104f0b2fcdee5350d180bc9980f Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Fri, 3 May 2024 07:41:05 +0000 Subject: [PATCH 29/47] update CHAGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fabbc0d..65c55b48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ [Read translated version (en)](./translations/en/CHANGELOG.md) # 未リリース分 +- `Date:year`系の関数に0を渡すと現在時刻になる問題を修正 # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように From 5c885520aad753e93a7496187407998e5b5b2ab5 Mon Sep 17 00:00:00 2001 From: uzmoi Date: Mon, 6 May 2024 18:16:21 +0900 Subject: [PATCH 30/47] =?UTF-8?q?fix:=20=E4=BD=8D=E7=BD=AE=E6=83=85?= =?UTF-8?q?=E5=A0=B1=E3=82=92=E4=BF=AE=E6=AD=A3=20(#648)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 位置情報を修正 * changelog * add test for location --- CHANGELOG.md | 3 ++- src/parser/parser.peggy | 5 ++--- test/index.ts | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65c55b48..5ef6934a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ # 未リリース分 - `Date:year`系の関数に0を渡すと現在時刻になる問題を修正 +- シンタックスエラーなどの位置情報を修正 # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように @@ -128,4 +129,4 @@ - 空の関数を定義できない問題を修正 - 空のスクリプトが許可されていない問題を修正 - ネームスペース付き変数のインクリメント、デクリメントを修正 -- ネームスペース付き変数への代入ができない問題を修正 +- ネームスペース付き変数への代入ができない問題を修正 diff --git a/src/parser/parser.peggy b/src/parser/parser.peggy index 4aacdbeb..7a95e63e 100644 --- a/src/parser/parser.peggy +++ b/src/parser/parser.peggy @@ -28,9 +28,8 @@ PreprocessPart / . Comment - = "//" (!EOL .)* { return ''; } - / "/*" (!"*/" .)* "*/" { return ''; } - + = "//" (!EOL .)* { return ' '.repeat(text().length); } + / "/*" (!"*/" .)* "*/" { return text().replace(/[^\n]/g, ' '); } // // main parser diff --git a/test/index.ts b/test/index.ts index 29873c4c..fed26c01 100644 --- a/test/index.ts +++ b/test/index.ts @@ -2399,6 +2399,20 @@ describe('Location', () => { if (!node.loc) assert.fail(); assert.deepEqual(node.loc, { start: 3, end: 13 }); }); + test.concurrent('comment', async () => { + let node: Ast.Node; + const parser = new Parser(); + const nodes = parser.parse(` + /* + */ + // hoge + @f(a) { a } + `); + assert.equal(nodes.length, 1); + node = nodes[0]; + if (!node.loc) assert.fail(); + assert.deepEqual(node.loc, { start: 23, end: 33 }); + }); }); describe('Variable declaration', () => { From dc7bfa370d9748c40363d7e63c1d8bf952da0d9f Mon Sep 17 00:00:00 2001 From: uzmoi Date: Mon, 6 May 2024 18:16:57 +0900 Subject: [PATCH 31/47] =?UTF-8?q?=E4=BA=8C=E9=A0=85=E6=BC=94=E7=AE=97?= =?UTF-8?q?=E5=AD=90=E3=81=ABLoc=E3=82=92=E8=BF=BD=E5=8A=A0=20(#647)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * call, and, orノードにlocを追加 * InfixにoperatorLocsプロパティーを追加 * 糖衣構文の二項演算子のノードにlocを追加 * and, orノードにlocを追加 * refactor * 型エイリアスを使用 --- etc/aiscript.api.md | 7 +++- src/node.ts | 2 ++ src/parser/node.ts | 10 +++--- src/parser/parser.peggy | 11 +++++-- src/parser/plugins/infix-to-fncall.ts | 47 ++++++++++++++++++--------- 5 files changed, 55 insertions(+), 22 deletions(-) diff --git a/etc/aiscript.api.md b/etc/aiscript.api.md index b35a710b..f94fdf10 100644 --- a/etc/aiscript.api.md +++ b/etc/aiscript.api.md @@ -72,6 +72,7 @@ type And = NodeBase & { type: 'and'; left: Expression; right: Expression; + operatorLoc: Loc; }; // @public (undocumented) @@ -79,6 +80,7 @@ type And_2 = NodeBase_2 & { type: 'and'; left: Expression_2; right: Expression_2; + operatorLoc: Loc; }; // @public (undocumented) @@ -556,6 +558,7 @@ type Infix = NodeBase_2 & { type: 'infix'; operands: Expression_2[]; operators: InfixOperator[]; + operatorLocs: Loc[]; }; // @public (undocumented) @@ -706,7 +709,7 @@ type Namespace_2 = NodeBase_2 & { // @public (undocumented) type Node_2 = Namespace | Meta | Statement | Expression | TypeSource; -// @public +// @public (undocumented) type Node_3 = Namespace_2 | Meta_2 | Statement_2 | Expression_2 | ChainMember | TypeSource_2; // @public @@ -778,6 +781,7 @@ type Or = NodeBase & { type: 'or'; left: Expression; right: Expression; + operatorLoc: Loc; }; // @public (undocumented) @@ -785,6 +789,7 @@ type Or_2 = NodeBase_2 & { type: 'or'; left: Expression_2; right: Expression_2; + operatorLoc: Loc; }; // @public (undocumented) diff --git a/src/node.ts b/src/node.ts index 79153c10..aebff7de 100644 --- a/src/node.ts +++ b/src/node.ts @@ -153,12 +153,14 @@ export type And = NodeBase & { type: 'and'; left: Expression; right: Expression; + operatorLoc: Loc; } export type Or = NodeBase & { type: 'or'; left: Expression; right: Expression; + operatorLoc: Loc; } export type If = NodeBase & { diff --git a/src/parser/node.ts b/src/parser/node.ts index b9a2dd77..a165a08d 100644 --- a/src/parser/node.ts +++ b/src/parser/node.ts @@ -6,6 +6,8 @@ * この処理結果がプラグインによって処理されるとASTノードとなります。 */ +import type { Loc } from '../node.js'; + export type Node = Namespace | Meta | Statement | Expression | ChainMember | TypeSource; export type Statement = @@ -59,10 +61,7 @@ export function isExpression(x: Node): x is Expression { type NodeBase = { __AST_NODE: never; // phantom type - loc?: { - start: number; - end: number; - }; + loc?: Loc; }; export type Namespace = NodeBase & { @@ -150,6 +149,7 @@ export type Infix = NodeBase & { type: 'infix'; operands: Expression[]; operators: InfixOperator[]; + operatorLocs: Loc[]; }; export type Not = NodeBase & { @@ -161,12 +161,14 @@ export type And = NodeBase & { type: 'and'; left: Expression; right: Expression; + operatorLoc: Loc; } export type Or = NodeBase & { type: 'or'; left: Expression; right: Expression; + operatorLoc: Loc; } export type If = NodeBase & { diff --git a/src/parser/parser.peggy b/src/parser/parser.peggy index 7a95e63e..0a9b27b2 100644 --- a/src/parser/parser.peggy +++ b/src/parser/parser.peggy @@ -282,7 +282,8 @@ Infix { return createNode('infix', { operands: [head, ...tail.map(i => i.term)], - operators: tail.map(i => i.op) + operators: tail.map(i => i.op.value), + operatorLocs: tail.map(i => i.op.loc), }); } @@ -292,7 +293,13 @@ InfixSp Op = ("||" / "&&" / "==" / "!=" / "<=" / ">=" / "<" / ">" / "+" / "-" / "*" / "^" / "/" / "%") -{ return text(); } +{ + const loc = location(); + return { + value: text(), + loc: { start: loc.start.offset, end: loc.end.offset - 1 }, + }; +} Not = "!" expr:Expr diff --git a/src/parser/plugins/infix-to-fncall.ts b/src/parser/plugins/infix-to-fncall.ts index 7d551320..d75dd4d2 100644 --- a/src/parser/plugins/infix-to-fncall.ts +++ b/src/parser/plugins/infix-to-fncall.ts @@ -1,6 +1,7 @@ import { visitNode } from '../visit.js'; import { AiScriptSyntaxError } from '../../error.js'; import type * as Cst from '../node.js'; +import type { Loc } from '../../node.js'; /** * 中置演算子式を表す木 @@ -15,6 +16,7 @@ type InfixTree = { left: InfixTree | Cst.Node; right: InfixTree | Cst.Node; info: { + opLoc: Loc; priority: number; // 優先度(高いほど優先して計算される値) } & ({ func: string; // 対応する関数名 @@ -76,15 +78,18 @@ function treeToNode(tree: InfixTree | Cst.Node): Cst.Node { if (tree.info.mapFn) { return tree.info.mapFn(tree); } else { + const left = treeToNode(tree.left); + const right = treeToNode(tree.right); return { type: 'call', - target: { type: 'identifier', name: tree.info.func }, - args: [treeToNode(tree.left), treeToNode(tree.right)], + target: { type: 'identifier', name: tree.info.func, loc: tree.info.opLoc }, + args: [left, right], + loc: { start: left.loc!.start,end: right.loc!.end }, } as Cst.Call; } } -const infoTable: Record = { +const infoTable: Record> = { '*': { func: 'Core:mul', priority: 7 }, '^': { func: 'Core:pow', priority: 7 }, '/': { func: 'Core:div', priority: 7 }, @@ -98,19 +103,31 @@ const infoTable: Record = { '<=': { func: 'Core:lteq', priority: 4 }, '>=': { func: 'Core:gteq', priority: 4 }, '&&': { - mapFn: infix => ({ - type: 'and', - left: treeToNode(infix.left), - right: treeToNode(infix.right), - }) as Cst.And, + mapFn: infix => { + const left = treeToNode(infix.left); + const right = treeToNode(infix.right); + return { + type: 'and', + left, + right, + loc: { start: left.loc!.start, end: right.loc!.end }, + operatorLoc: infix.info.opLoc, + } as Cst.And; + }, priority: 3, }, '||': { - mapFn: infix => ({ - type: 'or', - left: treeToNode(infix.left), - right: treeToNode(infix.right), - }) as Cst.Or, + mapFn: infix => { + const left = treeToNode(infix.left); + const right = treeToNode(infix.right); + return { + type: 'or', + left, + right, + loc: { start: left.loc!.start, end: right.loc!.end }, + operatorLoc: infix.info.opLoc, + } as Cst.Or; + }, priority: 3, }, }; @@ -119,12 +136,12 @@ const infoTable: Record = { * NInfix を関数呼び出し形式に変換する */ function transform(node: Cst.Infix): Cst.Node { - const infos = node.operators.map(op => { + const infos = node.operators.map((op, i) => { const info = infoTable[op]; if (info == null) { throw new AiScriptSyntaxError(`No such operator: ${op}.`); } - return info; + return { ...info, opLoc: node.operatorLocs[i] } as InfixTree['info']; }); let currTree = INFIX_TREE(node.operands[0]!, node.operands[1]!, infos[0]!); for (let i = 0; i < infos.length - 1; i++) { From 5e135c7985670ef4add47d832844cfeef56f2d2f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 19:38:17 +0000 Subject: [PATCH 32/47] Bump actions/checkout from 4.1.4 to 4.1.5 in the gh-actions group Bumps the gh-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 4.1.4 to 4.1.5 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4.1.4...v4.1.5) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gh-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/api.yml | 2 +- .github/workflows/gh-pages.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/publish-nightly-dev.yml | 2 +- .github/workflows/publish-nightly-next.yml | 2 +- .github/workflows/test.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index 338ccf10..0a912e4f 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.1.4 + uses: actions/checkout@v4.1.5 - name: Setup Node.js uses: actions/setup-node@v4.0.2 diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 622154b9..d1b88410 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.1.4 + uses: actions/checkout@v4.1.5 - name: Setup Node.js uses: actions/setup-node@v4.0.2 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a659fbbf..a3eb5538 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.1.4 + uses: actions/checkout@v4.1.5 - name: Setup Node.js uses: actions/setup-node@v4.0.2 diff --git a/.github/workflows/publish-nightly-dev.yml b/.github/workflows/publish-nightly-dev.yml index 688a7485..caff8cd9 100644 --- a/.github/workflows/publish-nightly-dev.yml +++ b/.github/workflows/publish-nightly-dev.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.1.4 + uses: actions/checkout@v4.1.5 with: ref: ${{ env.BRANCH }} diff --git a/.github/workflows/publish-nightly-next.yml b/.github/workflows/publish-nightly-next.yml index f041b9e9..62110872 100644 --- a/.github/workflows/publish-nightly-next.yml +++ b/.github/workflows/publish-nightly-next.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.1.4 + uses: actions/checkout@v4.1.5 with: ref: ${{ env.BRANCH }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3f2eb121..3e285926 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4.1.4 + uses: actions/checkout@v4.1.5 - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v4.0.2 From 1e67f131e678e3a976034e06e6a0b16c7fac80ac Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 9 May 2024 07:40:22 +0000 Subject: [PATCH 33/47] =?UTF-8?q?str.starts=5Fwith=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + docs/primitive-props.md | 7 +++++ src/interpreter/primitive-props.ts | 15 +++++++++++ test/index.ts | 42 ++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ef6934a..f004b87c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ # 未リリース分 - `Date:year`系の関数に0を渡すと現在時刻になる問題を修正 - シンタックスエラーなどの位置情報を修正 +- `str.starts_with`を追加 # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように diff --git a/docs/primitive-props.md b/docs/primitive-props.md index 609cc6ee..233b155c 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -62,6 +62,13 @@ Core:range(0,2).push(4) //[0,1,2,4] ### @(_v_: str).incl(_keyword_: str): bool 文字列中に _keyword_ が含まれていれば`true`、なければ`false`を返します。 +### @(_v_: str).starts_with(_prefix_: str, _start\_index_?: num): bool +文字列が _prefix_ で始まっていれば`true`、そうでなければ`false`を返します。\ +_prefix_ が空文字列の場合は常に`true`を返します。\ +_start\_index_ が指定されている場合、そのインデックスから始めます。\ +_start\_index_ が`v_.len`より大きいか`-v.len`より小さい場合は`false`を返します。\ +_start\_index_ が負の場合は末尾から数えます。 + ### @(_v_: str).slice(_begin_: num, _end_: num): str 文字列の _begin_ 番目から _end_ 番目の直前までの部分を取得します。 diff --git a/src/interpreter/primitive-props.ts b/src/interpreter/primitive-props.ts index c154c45a..2fa34826 100644 --- a/src/interpreter/primitive-props.ts +++ b/src/interpreter/primitive-props.ts @@ -119,6 +119,21 @@ const PRIMITIVE_PROPS: { const res = target.value.codePointAt(i.value) ?? target.value.charCodeAt(i.value); return Number.isNaN(res) ? NULL : NUM(res); }), + + starts_with: (target: VStr): VFn => FN_NATIVE(async ([prefix, start_index], _opts) => { + assertString(prefix); + if (!prefix.value) { + return TRUE; + } + + if (start_index) assertNumber(start_index); + const raw_index = start_index?.value ?? 0; + if (raw_index < -target.value.length || raw_index > target.value.length) { + return FALSE; + } + const index = (raw_index >= 0) ? raw_index : target.value.length + raw_index; + return target.value.startsWith(prefix.value, index) ? TRUE : FALSE; + }), }, arr: { diff --git a/test/index.ts b/test/index.ts index fed26c01..cb5f6250 100644 --- a/test/index.ts +++ b/test/index.ts @@ -2626,6 +2626,48 @@ describe('primitive props', () => { ARR([NUM(97), NUM(98), NUM(99), NUM(240), NUM(169), NUM(184), NUM(189), NUM(240), NUM(159), NUM(145), NUM(137), NUM(240), NUM(159), NUM(143), NUM(191), NUM(240), NUM(159), NUM(145), NUM(168), NUM(226), NUM(128), NUM(141), NUM(240), NUM(159), NUM(145), NUM(166), NUM(100), NUM(101), NUM(102)]) ); }); + + test.concurrent('starts_with (no index)', async () => { + const res = await exe(` + let str = "hello" + let empty = "" + <: [ + str.starts_with(""), str.starts_with("hello"), + str.starts_with("he"), str.starts_with("ell"), + empty.starts_with(""), empty.starts_with("he"), + ] + `); + eq(res, ARR([ + TRUE, TRUE, + TRUE, FALSE, + TRUE, FALSE, + ])); + }); + + test.concurrent('starts_with (with index)', async () => { + const res = await exe(` + let str = "hello" + let empty = "" + <: [ + str.starts_with("", 4), str.starts_with("he", 0), + str.starts_with("ll", 2), str.starts_with("lo", 3), + str.starts_with("lo", -2), str.starts_with("hel", -5), + str.starts_with("he", 2), str.starts_with("loa", 3), + str.starts_with("lo", -6), str.starts_with("", -7), + str.starts_with("lo", 6), str.starts_with("", 7), + empty.starts_with("", 2), empty.starts_with("ll", 2), + ] + `); + eq(res, ARR([ + TRUE, TRUE, + TRUE, TRUE, + TRUE, TRUE, + FALSE, FALSE, + FALSE, TRUE, + FALSE, TRUE, + TRUE, FALSE, + ])); + }); }); describe('arr', () => { From 32c6a4000d44539a45add9e1e5fbde5cac1f1239 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 9 May 2024 07:40:53 +0000 Subject: [PATCH 34/47] =?UTF-8?q?str.ends=5Fwith=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- docs/primitive-props.md | 7 +++++ src/interpreter/primitive-props.ts | 16 ++++++++++++ test/index.ts | 42 ++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f004b87c..bbdb8a1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ # 未リリース分 - `Date:year`系の関数に0を渡すと現在時刻になる問題を修正 - シンタックスエラーなどの位置情報を修正 -- `str.starts_with`を追加 +- `str.starts_with`,`str.ends_with`を追加 # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように diff --git a/docs/primitive-props.md b/docs/primitive-props.md index 233b155c..eaa27c63 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -69,6 +69,13 @@ _start\_index_ が指定されている場合、そのインデックスから _start\_index_ が`v_.len`より大きいか`-v.len`より小さい場合は`false`を返します。\ _start\_index_ が負の場合は末尾から数えます。 +### @(_v_: str).ends_with(_suffix_: str, _end\_index_?: num): bool +文字列が _suffix_ で終わっていれば`true`、そうでなければ`false`を返します。\ +_suffix_ が空文字列の場合は常に`true`を返します。\ +_end\_index_ が指定されている場合、そのインデックスの直前を末尾とします。\ +_end\_index_ が`v_.len`より大きいか`-v.len`より小さい場合は`false`を返します。\ +_end\_index_ が負の場合は末尾から数えます。 + ### @(_v_: str).slice(_begin_: num, _end_: num): str 文字列の _begin_ 番目から _end_ 番目の直前までの部分を取得します。 diff --git a/src/interpreter/primitive-props.ts b/src/interpreter/primitive-props.ts index 2fa34826..a5881f14 100644 --- a/src/interpreter/primitive-props.ts +++ b/src/interpreter/primitive-props.ts @@ -134,6 +134,22 @@ const PRIMITIVE_PROPS: { const index = (raw_index >= 0) ? raw_index : target.value.length + raw_index; return target.value.startsWith(prefix.value, index) ? TRUE : FALSE; }), + + ends_with: (target: VStr): VFn => FN_NATIVE(async ([suffix, end_index], _opts) => { + assertString(suffix); + if (!suffix.value) { + return TRUE; + } + + if (end_index) assertNumber(end_index); + const raw_index = end_index?.value ?? target.value.length; + if (raw_index < -target.value.length || raw_index > target.value.length) { + return FALSE; + } + const index = (raw_index >= 0) ? raw_index : target.value.length + raw_index; + + return target.value.endsWith(suffix.value, index) ? TRUE : FALSE; + }), }, arr: { diff --git a/test/index.ts b/test/index.ts index cb5f6250..7a21ee59 100644 --- a/test/index.ts +++ b/test/index.ts @@ -2668,6 +2668,48 @@ describe('primitive props', () => { TRUE, FALSE, ])); }); + + test.concurrent('ends_with (no index)', async () => { + const res = await exe(` + let str = "hello" + let empty = "" + <: [ + str.ends_with(""), str.ends_with("hello"), + str.ends_with("lo"), str.ends_with("ell"), + empty.ends_with(""), empty.ends_with("he"), + ] + `); + eq(res, ARR([ + TRUE, TRUE, + TRUE, FALSE, + TRUE, FALSE, + ])); + }); + + test.concurrent('ends_with (with index)', async () => { + const res = await exe(` + let str = "hello" + let empty = "" + <: [ + str.ends_with("", 3), str.ends_with("lo", 5), + str.ends_with("ll", 4), str.ends_with("he", 2), + str.ends_with("ll", -1), str.ends_with("he", -3), + str.ends_with("he", 5), str.ends_with("lo", 3), + str.ends_with("lo", -6), str.ends_with("", -7), + str.ends_with("lo", 6), str.ends_with("", 7), + empty.ends_with("", 2), empty.ends_with("ll", 2), + ] + `); + eq(res, ARR([ + TRUE, TRUE, + TRUE, TRUE, + TRUE, TRUE, + FALSE, FALSE, + FALSE, TRUE, + FALSE, TRUE, + TRUE, FALSE, + ])); + }); }); describe('arr', () => { From 11e5f013104a2003b71ae8bc1eedc0d3f701780e Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 9 May 2024 13:07:05 +0000 Subject: [PATCH 35/47] fix docs/primitive-props.md - str.starts_with - str.ends_with --- docs/primitive-props.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/primitive-props.md b/docs/primitive-props.md index eaa27c63..2e797c79 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -66,14 +66,14 @@ Core:range(0,2).push(4) //[0,1,2,4] 文字列が _prefix_ で始まっていれば`true`、そうでなければ`false`を返します。\ _prefix_ が空文字列の場合は常に`true`を返します。\ _start\_index_ が指定されている場合、そのインデックスから始めます。\ -_start\_index_ が`v_.len`より大きいか`-v.len`より小さい場合は`false`を返します。\ +_start\_index_ が`v.len`より大きいか`-v.len`より小さい場合は`false`を返します。\ _start\_index_ が負の場合は末尾から数えます。 ### @(_v_: str).ends_with(_suffix_: str, _end\_index_?: num): bool 文字列が _suffix_ で終わっていれば`true`、そうでなければ`false`を返します。\ _suffix_ が空文字列の場合は常に`true`を返します。\ -_end\_index_ が指定されている場合、そのインデックスの直前を末尾とします。\ -_end\_index_ が`v_.len`より大きいか`-v.len`より小さい場合は`false`を返します。\ +_end\_index_ が指定されている場合、そのインデックスの直前を末尾とします。(省略時は`v.len`)\ +_end\_index_ が`v.len`より大きいか`-v.len`より小さい場合は`false`を返します。\ _end\_index_ が負の場合は末尾から数えます。 ### @(_v_: str).slice(_begin_: num, _end_: num): str From 427a6d8453dcd4d28d1de9c120a4a90c54c6776e Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 9 May 2024 14:44:40 +0000 Subject: [PATCH 36/47] =?UTF-8?q?str.pad=5Fstart=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + docs/primitive-props.md | 5 +++++ src/interpreter/primitive-props.ts | 7 +++++++ test/index.ts | 16 ++++++++++++++++ 4 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ef6934a..57e5b9db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ # 未リリース分 - `Date:year`系の関数に0を渡すと現在時刻になる問題を修正 - シンタックスエラーなどの位置情報を修正 +- `str.pad_start`を追加 # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように diff --git a/docs/primitive-props.md b/docs/primitive-props.md index 609cc6ee..90e57528 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -78,6 +78,11 @@ _fromIndex_が指定されていれば、その位置から検索を開始しま _fromIndex_が負値の時は末尾からの位置(文字列の長さ+_fromIndex_)が使用されます。 該当が無ければ-1を返します。 +### @(_v_: str).pad_start(_width_: num, _pad_?: str): str +文字列の長さがが _width_ になるように、先頭を _pad_ の繰り返しで埋めた新しい文字列を返します。\ +_pad_ を省略した場合、空白`' '`で埋められます。\ +_pad_ が長すぎる場合、_pad_ の末尾が切り捨てられます。 + ### @(_v_: str).trim(): str 文字列の前後の空白を取り除いたものを返します。 diff --git a/src/interpreter/primitive-props.ts b/src/interpreter/primitive-props.ts index c154c45a..2c68d8e9 100644 --- a/src/interpreter/primitive-props.ts +++ b/src/interpreter/primitive-props.ts @@ -119,6 +119,13 @@ const PRIMITIVE_PROPS: { const res = target.value.codePointAt(i.value) ?? target.value.charCodeAt(i.value); return Number.isNaN(res) ? NULL : NUM(res); }), + + pad_start: (target: VStr): VFn => FN_NATIVE(([width, pad], _) => { + assertNumber(width); + const s = (pad) ? (assertString(pad), pad.value) : ' '; + + return STR(target.value.padStart(width.value, s)); + }), }, arr: { diff --git a/test/index.ts b/test/index.ts index fed26c01..b290296e 100644 --- a/test/index.ts +++ b/test/index.ts @@ -2626,6 +2626,22 @@ describe('primitive props', () => { ARR([NUM(97), NUM(98), NUM(99), NUM(240), NUM(169), NUM(184), NUM(189), NUM(240), NUM(159), NUM(145), NUM(137), NUM(240), NUM(159), NUM(143), NUM(191), NUM(240), NUM(159), NUM(145), NUM(168), NUM(226), NUM(128), NUM(141), NUM(240), NUM(159), NUM(145), NUM(166), NUM(100), NUM(101), NUM(102)]) ); }); + + test.concurrent("pad_start", async () => { + const res = await exe(` + let str = "abc" + <: [ + str.pad_start(0), str.pad_start(1), str.pad_start(2), str.pad_start(3), str.pad_start(4), str.pad_start(5), + str.pad_start(0, "0"), str.pad_start(1, "0"), str.pad_start(2, "0"), str.pad_start(3, "0"), str.pad_start(4, "0"), str.pad_start(5, "0"), + str.pad_start(0, "01"), str.pad_start(1, "01"), str.pad_start(2, "01"), str.pad_start(3, "01"), str.pad_start(4, "01"), str.pad_start(5, "01"), + ] + `); + eq(res, ARR([ + STR("abc"), STR("abc"), STR("abc"), STR("abc"), STR(" abc"), STR(" abc"), + STR("abc"), STR("abc"), STR("abc"), STR("abc"), STR("0abc"), STR("00abc"), + STR("abc"), STR("abc"), STR("abc"), STR("abc"), STR("0abc"), STR("01abc"), + ])); + }); }); describe('arr', () => { From e1b3a22aacb4cd71ca3b60f48935c0b331c4cf64 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 9 May 2024 14:49:17 +0000 Subject: [PATCH 37/47] =?UTF-8?q?str.pad=5Fend=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- docs/primitive-props.md | 5 +++++ src/interpreter/primitive-props.ts | 7 +++++++ test/index.ts | 16 ++++++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57e5b9db..385bde1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ # 未リリース分 - `Date:year`系の関数に0を渡すと現在時刻になる問題を修正 - シンタックスエラーなどの位置情報を修正 -- `str.pad_start`を追加 +- `str.pad_start`,`str.pad_end`を追加 # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように diff --git a/docs/primitive-props.md b/docs/primitive-props.md index 90e57528..1c71f5e9 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -83,6 +83,11 @@ _fromIndex_が負値の時は末尾からの位置(文字列の長さ+_fromInd _pad_ を省略した場合、空白`' '`で埋められます。\ _pad_ が長すぎる場合、_pad_ の末尾が切り捨てられます。 +### @(_v_: str).pad_end(_width_: num, _pad_?: str): str +文字列の長さがが _width_ になるように、末尾を _pad_ の繰り返しで埋めた新しい文字列を返します。\ +_pad_ を省略した場合、空白`' '`で埋められます。\ +_pad_ が長すぎる場合、_pad_ の末尾が切り捨てられます。 + ### @(_v_: str).trim(): str 文字列の前後の空白を取り除いたものを返します。 diff --git a/src/interpreter/primitive-props.ts b/src/interpreter/primitive-props.ts index 2c68d8e9..52a20104 100644 --- a/src/interpreter/primitive-props.ts +++ b/src/interpreter/primitive-props.ts @@ -126,6 +126,13 @@ const PRIMITIVE_PROPS: { return STR(target.value.padStart(width.value, s)); }), + + pad_end: (target: VStr): VFn => FN_NATIVE(([width, pad], _) => { + assertNumber(width); + const s = (pad) ? (assertString(pad), pad.value) : ' '; + + return STR(target.value.padEnd(width.value, s)); + }), }, arr: { diff --git a/test/index.ts b/test/index.ts index b290296e..93a51b0c 100644 --- a/test/index.ts +++ b/test/index.ts @@ -2642,6 +2642,22 @@ describe('primitive props', () => { STR("abc"), STR("abc"), STR("abc"), STR("abc"), STR("0abc"), STR("01abc"), ])); }); + + test.concurrent("pad_end", async () => { + const res = await exe(` + let str = "abc" + <: [ + str.pad_end(0), str.pad_end(1), str.pad_end(2), str.pad_end(3), str.pad_end(4), str.pad_end(5), + str.pad_end(0, "0"), str.pad_end(1, "0"), str.pad_end(2, "0"), str.pad_end(3, "0"), str.pad_end(4, "0"), str.pad_end(5, "0"), + str.pad_end(0, "01"), str.pad_end(1, "01"), str.pad_end(2, "01"), str.pad_end(3, "01"), str.pad_end(4, "01"), str.pad_end(5, "01"), + ] + `); + eq(res, ARR([ + STR("abc"), STR("abc"), STR("abc"), STR("abc"), STR("abc "), STR("abc "), + STR("abc"), STR("abc"), STR("abc"), STR("abc"), STR("abc0"), STR("abc00"), + STR("abc"), STR("abc"), STR("abc"), STR("abc"), STR("abc0"), STR("abc01"), + ])); + }); }); describe('arr', () => { From 4a18c33b18c0a5df9249529e5e3af0b25a8e89fe Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 9 May 2024 15:17:25 +0000 Subject: [PATCH 38/47] =?UTF-8?q?arr.insert=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + docs/primitive-props.md | 6 ++++++ src/interpreter/primitive-props.ts | 9 +++++++++ test/index.ts | 18 ++++++++++++++++++ 4 files changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ef6934a..eb7dbe2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ # 未リリース分 - `Date:year`系の関数に0を渡すと現在時刻になる問題を修正 - シンタックスエラーなどの位置情報を修正 +- `arr.insert`を追加 # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように diff --git a/docs/primitive-props.md b/docs/primitive-props.md index 609cc6ee..7f0df3f6 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -184,6 +184,12 @@ _fromIndex_ および _toIndex_ に関する挙動は`arr.slice`に準拠しま `arr.copy`同様シャローコピーであり、配列やオブジェクトの参照は維持されます。 _times_ には0以上の整数値を指定します。それ以外ではエラーになります。 +### @(_v_: arr).insert(_index_: num, _item_: value): null +**【この操作は配列を書き換えます】** +配列の _index_ の位置に _item_ を挿入します。\ +_index_ が負の場合は末尾から数えます。\ +_index_ が最後の要素より後の場合は末尾に追加します。 + ### @(_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 c154c45a..db40623e 100644 --- a/src/interpreter/primitive-props.ts +++ b/src/interpreter/primitive-props.ts @@ -301,6 +301,15 @@ 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; + }), }, error: { diff --git a/test/index.ts b/test/index.ts index fed26c01..26571233 100644 --- a/test/index.ts +++ b/test/index.ts @@ -2920,6 +2920,24 @@ 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] + <: [null,null,null,null,null,null,arr1] + `); + 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)]) + ])); + }); }); }); From 86fe815239c2b90cfb7067fcc3d5b405d8822335 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 9 May 2024 15:24:58 +0000 Subject: [PATCH 39/47] fix test - arr.insert --- test/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/index.ts b/test/index.ts index 26571233..6f34f61a 100644 --- a/test/index.ts +++ b/test/index.ts @@ -2931,7 +2931,8 @@ describe('primitive props', () => { 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] - <: [null,null,null,null,null,null,arr1] + res.push(arr1) + <: res `); eq(res, ARR([ NULL, NULL, NULL, NULL, NULL, NULL, From f6993b211524ae2c57c5de080481d4e789b43afd Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Thu, 9 May 2024 15:29:50 +0000 Subject: [PATCH 40/47] =?UTF-8?q?arr.remove=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- docs/primitive-props.md | 6 ++++++ src/interpreter/primitive-props.ts | 8 ++++++++ test/index.ts | 19 +++++++++++++++++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb7dbe2a..c137df0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ # 未リリース分 - `Date:year`系の関数に0を渡すと現在時刻になる問題を修正 - シンタックスエラーなどの位置情報を修正 -- `arr.insert`を追加 +- `arr.insert`,`arr.remove`を追加 # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように diff --git a/docs/primitive-props.md b/docs/primitive-props.md index 7f0df3f6..2b76d495 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -190,6 +190,12 @@ _times_ には0以上の整数値を指定します。それ以外ではエラ _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 db40623e..efda34b9 100644 --- a/src/interpreter/primitive-props.ts +++ b/src/interpreter/primitive-props.ts @@ -310,6 +310,14 @@ const PRIMITIVE_PROPS: { 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 6f34f61a..04796634 100644 --- a/test/index.ts +++ b/test/index.ts @@ -2939,6 +2939,25 @@ describe('primitive props', () => { 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)]) + ])); + }); }); }); From 5c7f11f763ab4e9a19ebadb9afc9a0621ad107c3 Mon Sep 17 00:00:00 2001 From: FineArchs <133759614+FineArchs@users.noreply.github.com> Date: Fri, 10 May 2024 22:26:51 +0900 Subject: [PATCH 41/47] =?UTF-8?q?=E7=A9=BA=E3=81=AE=E9=85=8D=E5=88=97?= =?UTF-8?q?=E3=82=92=E5=88=9D=E6=9C=9F=E5=80=A4=E3=81=AA=E3=81=97=E3=81=A7?= =?UTF-8?q?reduce=E3=81=99=E3=82=8B=E6=99=82=E3=82=A8=E3=83=A9=E3=83=BC?= =?UTF-8?q?=E3=82=92=E5=87=BA=E3=81=99=E3=82=88=E3=81=86=E3=81=AB=20(#600)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update primitive-props.ts * Update index.ts * fix * Update primitive-props.md * Update CHANGELOG.md --- CHANGELOG.md | 1 + docs/primitive-props.md | 4 +++- src/interpreter/primitive-props.ts | 1 + test/index.ts | 8 ++++++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ef6934a..17d265ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ # 未リリース分 - `Date:year`系の関数に0を渡すと現在時刻になる問題を修正 - シンタックスエラーなどの位置情報を修正 +- `arr.reduce`が空配列に対して初期値なしで呼び出された時、正式にエラーを出すよう # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように diff --git a/docs/primitive-props.md b/docs/primitive-props.md index 609cc6ee..1356e1c3 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -140,11 +140,13 @@ _i_ 番目の文字が存在しない場合は null が返されます。 配列の要素のうち _func_ が true を返すようなもののみを抜き出して返します。 順序は維持されます。 -### @(_v_: arr).reduce(_func_: @(_acm_: value, _item_: value, _index_: num) { value }, _initial_: value): value +### @(_v_: arr).reduce(_func_: Callback, _initial_: value): value +`Callback`: @(_acm_: value, _item_: value, _index_: num): value 配列の各要素に対し _func_ を順番に呼び出します。 各呼び出しでは、前回の結果が第1引数 _acm_ として渡されます。 _initial_ が指定された場合は初回呼び出しの引数が(_initial_, _v_\[0], 0)、 指定されなかった場合は(_v_\[0], _v_\[1], 1)となります。 +配列が空配列であり、かつ _initial_ が指定されていない場合はエラーになります。従って基本的には _initial_ を指定しておくことが推奨されています。 ### @(_v_: arr).find(_func_: @(_item_: value, _index_: num) { bool }): value 配列から _func_ が true を返すような要素を探し、その値を返します。 diff --git a/src/interpreter/primitive-props.ts b/src/interpreter/primitive-props.ts index c154c45a..f158546c 100644 --- a/src/interpreter/primitive-props.ts +++ b/src/interpreter/primitive-props.ts @@ -183,6 +183,7 @@ const PRIMITIVE_PROPS: { reduce: (target: VArr): VFn => FN_NATIVE(async ([fn, initialValue], opts) => { assertFunction(fn); const withInitialValue = initialValue != null; + if (!withInitialValue && (target.value.length === 0)) throw new AiScriptRuntimeError('Reduce of empty array without initial value'); let accumulator = withInitialValue ? initialValue : target.value[0]!; for (let i = withInitialValue ? 0 : 1; i < target.value.length; i++) { const item = target.value[i]!; diff --git a/test/index.ts b/test/index.ts index fed26c01..bfb716a4 100644 --- a/test/index.ts +++ b/test/index.ts @@ -4,6 +4,7 @@ */ import * as assert from 'assert'; +import { expect, test } from '@jest/globals'; import { Parser, Interpreter, utils, errors, Ast } from '../src'; import { NUM, STR, NULL, ARR, OBJ, BOOL, TRUE, FALSE, ERROR ,FN_NATIVE } from '../src/interpreter/value'; let { AiScriptRuntimeError, AiScriptIndexOutOfRangeError } = errors; @@ -2753,6 +2754,13 @@ describe('primitive props', () => { eq(res, NUM(20)); }); + test.concurrent('reduce of empty array without initial value', async () => { + await expect(exe(` + let arr = [1, 2, 3, 4] + <: [].reduce(@(){}) + `)).rejects.toThrow('Reduce of empty array without initial value'); + }); + test.concurrent('find', async () => { const res = await exe(` let arr = ["abc", "def", "ghi"] From 324fdddae2de004afe2bd52f597f7294b74fd758 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Mon, 13 May 2024 01:06:52 +0900 Subject: [PATCH 42/47] remove spaces index.ts --- test/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/index.ts b/test/index.ts index bbadfa16..18e215b9 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3105,7 +3105,7 @@ describe('primitive props', () => { ]), ])); }); - + test.concurrent('every', async () => { const res = await exe(` let arr1 = [0, 1, 2, 3] From 4cb7919f2593ee25625992341a0aa29577ed4645 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Mon, 13 May 2024 02:31:03 +0000 Subject: [PATCH 43/47] improve test: Math:gen_rng --- test/index.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/test/index.ts b/test/index.ts index 18e215b9..cb97391d 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3302,17 +3302,27 @@ describe('std', () => { test.concurrent('gen_rng', async () => { const res = await exe(` - @test(seed) { - let random = Math:gen_rng(seed) - return random(0 100) + @test(seed1, seed2) { + let n = 100 + let max = 100000 + let random1 = Math:gen_rng(seed1) + let random2 = Math:gen_rng(seed2) + var same = 0 + for n { + if random1(1, max) == random2(1, max) { + same += 1 + } + } + same / n } let seed1 = \`{Util:uuid()}\` let seed2 = \`{Date:year()}\` - let test1 = if (test(seed1) == test(seed1)) {true} else {false} - let test2 = if (test(seed1) == test(seed2)) {true} else {false} - <: [test1 test2] + let result = [] + result.push(test(seed1, seed1) == 1) + result.push(test(seed1, seed2) < 0.05) + <: result `) - eq(res, ARR([BOOL(true), BOOL(false)])); + eq(res, ARR([BOOL(true), BOOL(true)])); }); }); From e758cef712a6b00439fe1ac84109ae57e58407e3 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Mon, 13 May 2024 13:05:20 +0900 Subject: [PATCH 44/47] =?UTF-8?q?Math:gen=5Frng=E3=81=AE=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=81=AB=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/index.ts b/test/index.ts index cb97391d..30ec18ef 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3301,6 +3301,7 @@ describe('std', () => { }); test.concurrent('gen_rng', async () => { + // 2つのシード値から1~maxの乱数をn回生成して一致率を見る const res = await exe(` @test(seed1, seed2) { let n = 100 From f4f860324e9c6df5f5db0f17096e688a59106da2 Mon Sep 17 00:00:00 2001 From: salano_ym <53254905+salano-ym@users.noreply.github.com> Date: Mon, 13 May 2024 17:29:32 +0900 Subject: [PATCH 45/47] refactor index.ts --- test/index.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/index.ts b/test/index.ts index 30ec18ef..fd235f22 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3306,6 +3306,7 @@ describe('std', () => { @test(seed1, seed2) { let n = 100 let max = 100000 + let threshold = 0.05 let random1 = Math:gen_rng(seed1) let random2 = Math:gen_rng(seed2) var same = 0 @@ -3314,14 +3315,16 @@ describe('std', () => { same += 1 } } - same / n + let rate = same / n + if seed1 == seed2 { rate == 1 } + else { rate < threshold } } let seed1 = \`{Util:uuid()}\` let seed2 = \`{Date:year()}\` - let result = [] - result.push(test(seed1, seed1) == 1) - result.push(test(seed1, seed2) < 0.05) - <: result + <: [ + test(seed1, seed1) + test(seed1, seed2) + ] `) eq(res, ARR([BOOL(true), BOOL(true)])); }); From 38f36ef2cef10ecc7620db9f0f3d46c92d23abc6 Mon Sep 17 00:00:00 2001 From: FineArchs <133759614+FineArchs@users.noreply.github.com> Date: Tue, 14 May 2024 20:14:37 +0900 Subject: [PATCH 46/47] =?UTF-8?q?=E9=96=A2=E6=95=B0=E3=81=AE=E7=9C=81?= =?UTF-8?q?=E7=95=A5=E3=81=95=E3=82=8C=E3=81=9F=E5=BC=95=E6=95=B0=E3=81=AB?= =?UTF-8?q?NULL=E3=82=92=E6=A0=BC=E7=B4=8D=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=20(#639)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update index.ts * add test * Update CHANGELOG.md --- CHANGELOG.md | 2 ++ src/interpreter/index.ts | 2 +- test/index.ts | 10 ++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65815163..8d1626a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ - `Uri:encode_full`, `Uri:encode_component`, `Uri:decode_full`, `Uri:decode_component`を追加 - `str.starts_with`,`str.ends_with`を追加 - `arr.splice`を追加 +- 関数の省略された引数にNULLを格納するように + - 一時的な措置であり、省略可能引数構文の実装と同時に廃止予定です。依存し過ぎないようにしてください # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように diff --git a/src/interpreter/index.ts b/src/interpreter/index.ts index 23df41ea..496e16ff 100644 --- a/src/interpreter/index.ts +++ b/src/interpreter/index.ts @@ -239,7 +239,7 @@ export class Interpreter { for (let i = 0; i < (fn.args ?? []).length; i++) { _args.set(fn.args![i]!, { isMutable: true, - value: args[i]!, + value: args[i] ?? NULL, }); } const fnScope = fn.scope!.createChildScope(_args); diff --git a/test/index.ts b/test/index.ts index 18e215b9..91492317 100644 --- a/test/index.ts +++ b/test/index.ts @@ -1297,6 +1297,16 @@ describe('Function call', () => { } assert.fail(); }); + + test.concurrent('omitted args', async () => { + const res = await exe(` + @f(x, y) { + [x, y] + } + <: f(1) + `); + eq(res, ARR([NUM(1), NULL])); + }); }); describe('Return', () => { From f72da1b037ee5e9b03310ab4351d98491d70472d Mon Sep 17 00:00:00 2001 From: FineArchs <133759614+FineArchs@users.noreply.github.com> Date: Fri, 17 May 2024 18:19:54 +0900 Subject: [PATCH 47/47] =?UTF-8?q?CHANGELOG=E3=81=AE=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E3=82=92unreleased=E3=83=95=E3=82=A9=E3=83=AB=E3=83=80?= =?UTF-8?q?=E5=86=85=E3=81=A7=E8=A1=8C=E3=81=86=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=99=E3=82=8B=20(#666)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * create pre-release workflow * refactor * fix * time order * 消し忘れ --- .github/workflows/publish-nightly-next.yml | 56 ---------- ...sh-nightly-dev.yml => publish-nightly.yml} | 28 +++-- CHANGELOG.md | 14 --- package-lock.json | 75 ++++++++++--- package.json | 5 +- scripts/check-release.mjs | 8 ++ scripts/gen-pkg-ts.mjs | 4 +- scripts/pre-release.mjs | 101 ++++++++++++++++++ unreleased/.gitkeep | 0 unreleased/past.md | 10 ++ 10 files changed, 202 insertions(+), 99 deletions(-) delete mode 100644 .github/workflows/publish-nightly-next.yml rename .github/workflows/{publish-nightly-dev.yml => publish-nightly.yml} (66%) create mode 100644 scripts/check-release.mjs create mode 100644 scripts/pre-release.mjs create mode 100644 unreleased/.gitkeep create mode 100644 unreleased/past.md diff --git a/.github/workflows/publish-nightly-next.yml b/.github/workflows/publish-nightly-next.yml deleted file mode 100644 index 62110872..00000000 --- a/.github/workflows/publish-nightly-next.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Publish nightly (next) - -on: - schedule: - - cron: '50 18 * * *' - workflow_dispatch: - -jobs: - publish: - runs-on: ubuntu-latest - env: - NPM_SECRET: ${{ secrets.NPM_SECRET }} - BRANCH: aiscript-next - TAG: next - - steps: - - name: Checkout - uses: actions/checkout@v4.1.5 - with: - ref: ${{ env.BRANCH }} - - - name: Setup Node.js - uses: actions/setup-node@v4.0.2 - with: - node-version: 20.x - - - name: Cache dependencies - uses: actions/cache@v4 - with: - path: ~/.npm - key: npm-${{ hashFiles('package-lock.json') }} - restore-keys: npm- - - - name: Install dependencies - run: npm ci - - - name: Build - run: npm run build - - - name: Set Version - run: | - TIME_STAMP=$( date +'%Y%m%d' ) - VERSION_SUFFIX=-$TAG.$TIME_STAMP - vim package.json '+/"version"' '+s/:\s*".*\zs\ze"/'$VERSION_SUFFIX/ '+wq' - - - name: Check Commits - run: | - echo 'LAST_COMMITS='$( git log --since '24 hours ago' | wc -c ) >> $GITHUB_ENV - - - name: Publish - uses: JS-DevTools/npm-publish@v3 - if: ${{ env.NPM_SECRET != '' && env.LAST_COMMITS != 0 }} - with: - token: ${{ env.NPM_SECRET }} - tag: ${{ env.TAG }} - access: public diff --git a/.github/workflows/publish-nightly-dev.yml b/.github/workflows/publish-nightly.yml similarity index 66% rename from .github/workflows/publish-nightly-dev.yml rename to .github/workflows/publish-nightly.yml index caff8cd9..6669ba33 100644 --- a/.github/workflows/publish-nightly-dev.yml +++ b/.github/workflows/publish-nightly.yml @@ -1,4 +1,4 @@ -name: Publish nightly (dev) +name: Publish nightly on: schedule: @@ -8,16 +8,22 @@ on: jobs: publish: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - branch: master + tag: dev + - branch: aiscript-next + tag: next env: NPM_SECRET: ${{ secrets.NPM_SECRET }} - BRANCH: master - TAG: dev steps: - - name: Checkout + - name: Checkout ${{ matrix.branch }} uses: actions/checkout@v4.1.5 with: - ref: ${{ env.BRANCH }} + ref: ${{ matrix.branch }} - name: Setup Node.js uses: actions/setup-node@v4.0.2 @@ -34,23 +40,23 @@ jobs: - name: Install dependencies run: npm ci - - name: Build - run: npm run build - - name: Set Version run: | + CURRENT_VER=$(npm view 'file:.' version) TIME_STAMP=$( date +'%Y%m%d' ) - VERSION_SUFFIX=-$TAG.$TIME_STAMP - vim package.json '+/"version"' '+s/:\s*".*\zs\ze"/'$VERSION_SUFFIX/ '+wq' + echo 'NEWVERSION='$CURRENT_VER-${{ matrix.tag }}.$TIME_STAMP >> $GITHUB_ENV - name: Check Commits run: | echo 'LAST_COMMITS='$( git log --since '24 hours ago' | wc -c ) >> $GITHUB_ENV + - name: Prepare Publish + run: npm run pre-release + - name: Publish uses: JS-DevTools/npm-publish@v3 if: ${{ env.NPM_SECRET != '' && env.LAST_COMMITS != 0 }} with: token: ${{ env.NPM_SECRET }} - tag: ${{ env.TAG }} + tag: ${{ matrix.tag }} access: public diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d1626a9..bc8ae5f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,5 @@ [Read translated version (en)](./translations/en/CHANGELOG.md) -# 未リリース分 -- `Date:year`系の関数に0を渡すと現在時刻になる問題を修正 -- シンタックスエラーなどの位置情報を修正 -- `arr.reduce`が空配列に対して初期値なしで呼び出された時、正式にエラーを出すよう -- `str.pad_start`,`str.pad_end`を追加 -- `arr.insert`,`arr.remove`を追加 -- `arr.sort`の処理を非同期的にして高速化 -- `arr.flat`,`arr.flat_map`を追加 -- `Uri:encode_full`, `Uri:encode_component`, `Uri:decode_full`, `Uri:decode_component`を追加 -- `str.starts_with`,`str.ends_with`を追加 -- `arr.splice`を追加 -- 関数の省略された引数にNULLを格納するように - - 一時的な措置であり、省略可能引数構文の実装と同時に廃止予定です。依存し過ぎないようにしてください - # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように - `index_of`の配列版を追加 diff --git a/package-lock.json b/package-lock.json index 5d153106..15b55102 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@syuilo/aiscript", - "version": "0.18.0", + "version": "0.19.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@syuilo/aiscript", - "version": "0.18.0", + "version": "0.19.0", "license": "MIT", "dependencies": { "seedrandom": "3.0.5", @@ -27,6 +27,7 @@ "eslint-plugin-import": "2.29.1", "jest": "29.7.0", "peggy": "4.0.2", + "semver": "7.6.2", "ts-jest": "29.1.2", "ts-jest-resolver": "2.0.1", "ts-node": "10.9.2", @@ -1504,6 +1505,21 @@ "node": "*" } }, + "node_modules/@microsoft/api-extractor/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@microsoft/tsdoc": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", @@ -1619,6 +1635,21 @@ } } }, + "node_modules/@rushstack/node-core-library/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@rushstack/rig-package": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.2.tgz", @@ -7178,13 +7209,10 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -9360,6 +9388,15 @@ "requires": { "brace-expansion": "^1.1.7" } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } } } }, @@ -9462,6 +9499,17 @@ "resolve": "~1.22.1", "semver": "~7.5.4", "z-schema": "~5.0.2" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "@rushstack/rig-package": { @@ -13540,13 +13588,10 @@ "version": "3.0.5" }, "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true }, "shebang-command": { "version": "2.0.0", diff --git a/package.json b/package.json index 273c1231..7e069506 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,9 @@ "lint": "eslint . --ext .js,.jsx,.ts,.tsx", "jest": "jest --coverage --detectOpenHandles", "tsd": "tsd", - "test": "npm run jest" + "test": "npm run jest", + "pre-release": "node scripts/pre-release.mjs && npm run build", + "prepublishOnly": "node scripts/check-release.mjs" }, "devDependencies": { "@microsoft/api-extractor": "7.42.3", @@ -49,6 +51,7 @@ "eslint-plugin-import": "2.29.1", "jest": "29.7.0", "peggy": "4.0.2", + "semver": "7.6.2", "ts-jest": "29.1.2", "ts-jest-resolver": "2.0.1", "ts-node": "10.9.2", diff --git a/scripts/check-release.mjs b/scripts/check-release.mjs new file mode 100644 index 00000000..4d218aeb --- /dev/null +++ b/scripts/check-release.mjs @@ -0,0 +1,8 @@ +import { readdir } from 'node:fs/promises'; + +await readdir('./unreleased') + .then(pathes => { + if (pathes.length > 1 || (pathes.length === 1 && pathes[0] !== '.gitkeep')) throw new Error('Run "npm run pre-release" before publish.') + }, err => { + if (err.code !== 'ENOENT') throw err; + }); diff --git a/scripts/gen-pkg-ts.mjs b/scripts/gen-pkg-ts.mjs index f6d757a3..2e22d069 100644 --- a/scripts/gen-pkg-ts.mjs +++ b/scripts/gen-pkg-ts.mjs @@ -1,6 +1,6 @@ -import { writeFile } from 'node:fs/promises'; -import pkg from '../package.json' assert { type: 'json' }; +import { readFile, writeFile } from 'node:fs/promises'; +const pkg = JSON.parse((await readFile('./package.json', 'utf8'))); await writeFile('./src/pkg.ts', `/* This file is automatically generated by scripts/gen-pkg-ts.js. diff --git a/scripts/pre-release.mjs b/scripts/pre-release.mjs new file mode 100644 index 00000000..1dfd7dfb --- /dev/null +++ b/scripts/pre-release.mjs @@ -0,0 +1,101 @@ +import { readFile, readdir, writeFile, mkdir, rm } from 'node:fs/promises'; +import { env } from 'node:process'; +import { promisify } from 'node:util'; +import child_process from 'node:child_process'; +import semverValid from 'semver/functions/valid.js'; + + +const exec = promisify(child_process.exec); +const FILES = { + chlog: './CHANGELOG.md', + chlogs: './unreleased', + pkgjson: './package.json', +}; +const enc = { encoding: env.ENCODING ?? 'utf8' }; +const pkgjson = JSON.parse(await readFile(FILES.pkgjson, enc)); +const newver = (() => { + const newverCandidates = [ + [env.NEWVERSION, 'Environment variable NEWVERSION'], + [pkgjson.version, "Package.json's version field"], + ]; + for (const [ver, name] of newverCandidates) { + if (ver) { + if (semverValid(ver)) return ver; + else throw new Error(`${name} is set to "${ver}"; it is not valid`); + } + } + throw new Error('No effective version setting detected.'); +})(); +const actions = {}; + +/* + * Update package.json's version field + */ +actions.updatePackageJson = { + async read() { + return JSON.stringify( + { ...pkgjson, version: newver }, + null, '\t' + ); + }, + async write(json) { + return writeFile(FILES.pkgjson, json); + }, +}; + +/* + * Collect changelogs + */ +actions.collectChangeLogs = { + async read() { + const getNewLog = async () => { + const pathes = (await readdir(FILES.chlogs)).map(path => `${FILES.chlogs}/${path}`); + const pathesLastUpdate = await Promise.all( + pathes.map(async (path) => { + const gittime = Number((await exec( + `git log -1 --pretty="format:%ct" "${path}"` + )).stdout); + if (gittime) return { path, lastUpdate: gittime }; + else { + console.log(`Warning: git timestamp of "${path}" was not detected`); + return { path, lastUpdate: Infinity } + } + }) + ); + pathesLastUpdate.sort((a, b) => a.lastUpdate - b.lastUpdate); + const logPromises = pathesLastUpdate.map(({ path }) => readFile(path, enc)); + const logs = await Promise.all(logPromises); + return logs.map(v => v.trim()).join('\n'); + }; + const getOldLog = async () => { + const log = await readFile(FILES.chlog, enc); + const idx = log.indexOf('#'); + return [ + log.slice(0, idx), + log.slice(idx), + ]; + }; + const [newLog, [logHead, oldLog]] = await Promise.all([ getNewLog(), getOldLog() ]); + return `${logHead}# ${newver}\n${newLog}\n\n${oldLog}`; + + }, + async write(logs) { + return Promise.all([ + writeFile(FILES.chlog, logs), + rm(FILES.chlogs, { + recursive: true, + force: true, + }).then(() => + mkdir(FILES.chlogs) + ).then(() => + writeFile(`${FILES.chlogs}/.gitkeep`, '')) + ]); + }, +}; + +// read all before writing +const reads = await Promise.all(Object.entries(actions).map(async ([name, { read }]) => [name, await read().catch(err => { throw new Error(`in actions.${name}.read: ${err}`) })])); + +// write after reading all +await Promise.all(reads.map(([name, read]) => actions[name].write(read).catch(err => { throw new Error(`in actions.${name}.write: ${err}`) }))); + diff --git a/unreleased/.gitkeep b/unreleased/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/unreleased/past.md b/unreleased/past.md new file mode 100644 index 00000000..52191011 --- /dev/null +++ b/unreleased/past.md @@ -0,0 +1,10 @@ +- `Date:year`系の関数に0を渡すと現在時刻になる問題を修正 +- シンタックスエラーなどの位置情報を修正 +- `arr.reduce`が空配列に対して初期値なしで呼び出された時、正式にエラーを出すよう +- `str.pad_start`,`str.pad_end`を追加 +- `arr.insert`,`arr.remove`を追加 +- `arr.sort`の処理を非同期的にして高速化 +- `arr.flat`,`arr.flat_map`を追加 +- `Uri:encode_full`, `Uri:encode_component`, `Uri:decode_full`, `Uri:decode_component`を追加 +- `str.starts_with`,`str.ends_with`を追加 +- `arr.splice`を追加