From 6f225ae06f88fda8101e91c439ca668d028cdc31 Mon Sep 17 00:00:00 2001 From: takejohn Date: Wed, 24 Sep 2025 21:07:29 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=E3=83=97=E3=83=AD=E3=83=91=E3=83=86?= =?UTF-8?q?=E3=82=A3=E3=82=A2=E3=82=AF=E3=82=BB=E3=82=B9=E3=81=AE=E3=83=89?= =?UTF-8?q?=E3=83=83=E3=83=88=E8=A8=98=E6=B3=95=E3=81=AB=E4=BA=88=E7=B4=84?= =?UTF-8?q?=E8=AA=9E=E3=82=84=E6=96=87=E5=AD=97=E5=88=97=E3=82=92=E4=BD=BF?= =?UTF-8?q?=E3=81=88=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/parser/plugins/validate-keyword.ts | 3 +- src/parser/syntaxes/expressions.ts | 3 +- test/identifiers.ts | 43 ++++++++++++++++++++++---- test/index.ts | 12 +++---- 4 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/parser/plugins/validate-keyword.ts b/src/parser/plugins/validate-keyword.ts index ca79dc59..7ca8358d 100644 --- a/src/parser/plugins/validate-keyword.ts +++ b/src/parser/plugins/validate-keyword.ts @@ -128,8 +128,7 @@ function validateNode(node: Ast.Node): Ast.Node { } case 'ns': case 'attr': - case 'identifier': - case 'prop': { + case 'identifier': { validateName(node.name, node.loc.start); break; } diff --git a/src/parser/syntaxes/expressions.ts b/src/parser/syntaxes/expressions.ts index 1b638781..48b76b36 100644 --- a/src/parser/syntaxes/expressions.ts +++ b/src/parser/syntaxes/expressions.ts @@ -103,8 +103,7 @@ function parseInfix(s: ITokenStream, left: Ast.Expression, minBp: number): Ast.E } if (op === TokenKind.Dot) { - s.expect(TokenKind.Identifier); - const name = s.getTokenValue(); + const name = parseObjectKey(s); s.next(); return NODE('prop', { diff --git a/test/identifiers.ts b/test/identifiers.ts index 9b676b8e..23b56d8f 100644 --- a/test/identifiers.ts +++ b/test/identifiers.ts @@ -204,12 +204,6 @@ const sampleCodes = Object.entries<[(definedName: string, referredName: string) <: ${referredName}:f() `, NUM(1)], - prop: [(definedName, referredName) => - ` - let x = { ${definedName}: 1 } - x.${referredName} - `, NUM(1)], - meta: [(definedName) => ` ### ${definedName} 1 @@ -303,6 +297,43 @@ describe.each( }); }); +describe('identifier validation on obj key', () => { + const codes: [string, (definedName: string, referredName: string) => string][] = [ + ['literal', (definedName: string, referredName: string) => ` + let x = { ${definedName}: 1 } + <: x["${referredName}"] + `], + + ['prop', (definedName: string, referredName: string) => ` + let x = {} + x.${definedName} = 1 + <: x.${referredName} + `], + ] + + describe.each(codes)('%s', (_, code) => { + test.concurrent.each( + reservedWords + )('reserved word %s must be allowed', async (word) => { + const res = await exe(code(word, word)); + eq(res, NUM(1)); + }); + + test.concurrent.each( + identifierCases + )('%s is allowed: %s', async (word, allowed) => { + expect.hasAssertions(); + if (allowed) { + const res = await exe(code(word, word)); + eq(res, NUM(1)); + } else { + expect(() => parser.parse(code(word, word))).toThrow(AiScriptSyntaxError); + await Promise.resolve(); // https://github.com/vitest-dev/vitest/issues/4750 + } + }); + }); +}); + test.concurrent('Keyword cannot contain escape characters', async () => { await expect(async () => await exe(` \\u0069\\u0066 true { diff --git a/test/index.ts b/test/index.ts index fa158707..b94fd9ac 100644 --- a/test/index.ts +++ b/test/index.ts @@ -197,9 +197,7 @@ describe('Object', () => { ]))); }); - /* 未実装 - * see also: test/literals.ts > literal > obj (string key) - * issue: https://github.com/aiscript-dev/aiscript/issues/62 + // see also: test/literals.ts > literal > obj (string key) test.concurrent('string key', async () => { const res = await exe(` let obj = { @@ -222,7 +220,10 @@ describe('Object', () => { eq(res, NUM(42)); }); - test.concurrent('expression key', async () => { + // 未実装 + // issues: https://github.com/aiscript-dev/aiscript/issues/62 + // https://github.com/aiscript-dev/aiscript/issues/225 + test.concurrent.skip('expression key', async () => { const res = await exe(` let key = "藍" @@ -234,7 +235,6 @@ describe('Object', () => { `); eq(res, NUM(42)); }); - */ }); describe('Array', () => { @@ -1092,7 +1092,7 @@ describe('Security', () => { `); assert.fail(); } catch (e) { - assert.ok(e instanceof AiScriptSyntaxError); + assert.ok(e instanceof AiScriptRuntimeError); } try { From f4a9df460cdd2092e7084c87ad4f09da9278e40e Mon Sep 17 00:00:00 2001 From: takejohn Date: Wed, 24 Sep 2025 21:49:12 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=E4=BA=88=E7=B4=84=E8=AA=9E=E3=81=AE?= =?UTF-8?q?=E6=96=87=E5=AD=97=E5=88=97=E3=83=AA=E3=83=86=E3=83=A9=E3=83=AB?= =?UTF-8?q?=E3=81=AE=E3=83=86=E3=82=B9=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/identifiers.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/identifiers.ts b/test/identifiers.ts index 23b56d8f..1097558d 100644 --- a/test/identifiers.ts +++ b/test/identifiers.ts @@ -334,6 +334,30 @@ describe('identifier validation on obj key', () => { }); }); +describe('reserved word validation on string obj key', () => { + const codes: [string, (definedName: string, referredName: string) => string][] = [ + ['literal', (definedName: string, referredName: string) => ` + let x = { "${definedName}": 1 } + <: x["${referredName}"] + `], + + ['prop', (definedName: string, referredName: string) => ` + let x = {} + x."${definedName}" = 1 + <: x."${referredName}" + `], + ] + + describe.each(codes)('%s', (_, code) => { + test.concurrent.each( + reservedWords + )('reserved word %s must be allowed', async (word) => { + const res = await exe(code(word, word)); + eq(res, NUM(1)); + }); + }); +}); + test.concurrent('Keyword cannot contain escape characters', async () => { await expect(async () => await exe(` \\u0069\\u0066 true { From 279eaf89d35f2eb8c7c6af9dcd9a64fc5ac0e7c4 Mon Sep 17 00:00:00 2001 From: takejohn Date: Wed, 24 Sep 2025 21:51:49 +0900 Subject: [PATCH 3/3] CHANGELOG --- unreleased/reserved-word-keys.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 unreleased/reserved-word-keys.md diff --git a/unreleased/reserved-word-keys.md b/unreleased/reserved-word-keys.md new file mode 100644 index 00000000..1cf6144d --- /dev/null +++ b/unreleased/reserved-word-keys.md @@ -0,0 +1,2 @@ +- プロパティアクセスのドット記法に予約語を記述できるようになりました。 +- プロパティアクセスのドット記法に文字列リテラルを記述できるようになりました。