Skip to content

Commit 7372dbe

Browse files
authored
enhance: プロパティアクセスのドット記法に予約語や文字列を使えるように (#979)
* プロパティアクセスのドット記法に予約語や文字列を使えるように * 予約語の文字列リテラルのテスト * CHANGELOG
1 parent 008538a commit 7372dbe

File tree

5 files changed

+71
-16
lines changed

5 files changed

+71
-16
lines changed

src/parser/plugins/validate-keyword.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,7 @@ function validateNode(node: Ast.Node): Ast.Node {
128128
}
129129
case 'ns':
130130
case 'attr':
131-
case 'identifier':
132-
case 'prop': {
131+
case 'identifier': {
133132
validateName(node.name, node.loc.start);
134133
break;
135134
}

src/parser/syntaxes/expressions.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,7 @@ function parseInfix(s: ITokenStream, left: Ast.Expression, minBp: number): Ast.E
103103
}
104104

105105
if (op === TokenKind.Dot) {
106-
s.expect(TokenKind.Identifier);
107-
const name = s.getTokenValue();
106+
const name = parseObjectKey(s);
108107
s.next();
109108

110109
return NODE('prop', {

test/identifiers.ts

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,6 @@ const sampleCodes = Object.entries<[(definedName: string, referredName: string)
204204
<: ${referredName}:f()
205205
`, NUM(1)],
206206

207-
prop: [(definedName, referredName) =>
208-
`
209-
let x = { ${definedName}: 1 }
210-
x.${referredName}
211-
`, NUM(1)],
212-
213207
meta: [(definedName) =>
214208
`
215209
### ${definedName} 1
@@ -303,6 +297,67 @@ describe.each(
303297
});
304298
});
305299

300+
describe('identifier validation on obj key', () => {
301+
const codes: [string, (definedName: string, referredName: string) => string][] = [
302+
['literal', (definedName: string, referredName: string) => `
303+
let x = { ${definedName}: 1 }
304+
<: x["${referredName}"]
305+
`],
306+
307+
['prop', (definedName: string, referredName: string) => `
308+
let x = {}
309+
x.${definedName} = 1
310+
<: x.${referredName}
311+
`],
312+
]
313+
314+
describe.each(codes)('%s', (_, code) => {
315+
test.concurrent.each(
316+
reservedWords
317+
)('reserved word %s must be allowed', async (word) => {
318+
const res = await exe(code(word, word));
319+
eq(res, NUM(1));
320+
});
321+
322+
test.concurrent.each(
323+
identifierCases
324+
)('%s is allowed: %s', async (word, allowed) => {
325+
expect.hasAssertions();
326+
if (allowed) {
327+
const res = await exe(code(word, word));
328+
eq(res, NUM(1));
329+
} else {
330+
expect(() => parser.parse(code(word, word))).toThrow(AiScriptSyntaxError);
331+
await Promise.resolve(); // https://github.com/vitest-dev/vitest/issues/4750
332+
}
333+
});
334+
});
335+
});
336+
337+
describe('reserved word validation on string obj key', () => {
338+
const codes: [string, (definedName: string, referredName: string) => string][] = [
339+
['literal', (definedName: string, referredName: string) => `
340+
let x = { "${definedName}": 1 }
341+
<: x["${referredName}"]
342+
`],
343+
344+
['prop', (definedName: string, referredName: string) => `
345+
let x = {}
346+
x."${definedName}" = 1
347+
<: x."${referredName}"
348+
`],
349+
]
350+
351+
describe.each(codes)('%s', (_, code) => {
352+
test.concurrent.each(
353+
reservedWords
354+
)('reserved word %s must be allowed', async (word) => {
355+
const res = await exe(code(word, word));
356+
eq(res, NUM(1));
357+
});
358+
});
359+
});
360+
306361
test.concurrent('Keyword cannot contain escape characters', async () => {
307362
await expect(async () => await exe(`
308363
\\u0069\\u0066 true {

test/index.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,7 @@ describe('Object', () => {
197197
])));
198198
});
199199

200-
/* 未実装
201-
* see also: test/literals.ts > literal > obj (string key)
202-
* issue: https://github.com/aiscript-dev/aiscript/issues/62
200+
// see also: test/literals.ts > literal > obj (string key)
203201
test.concurrent('string key', async () => {
204202
const res = await exe(`
205203
let obj = {
@@ -222,7 +220,10 @@ describe('Object', () => {
222220
eq(res, NUM(42));
223221
});
224222

225-
test.concurrent('expression key', async () => {
223+
// 未実装
224+
// issues: https://github.com/aiscript-dev/aiscript/issues/62
225+
// https://github.com/aiscript-dev/aiscript/issues/225
226+
test.concurrent.skip('expression key', async () => {
226227
const res = await exe(`
227228
let key = "藍"
228229
@@ -234,7 +235,6 @@ describe('Object', () => {
234235
`);
235236
eq(res, NUM(42));
236237
});
237-
*/
238238
});
239239

240240
describe('Array', () => {
@@ -1092,7 +1092,7 @@ describe('Security', () => {
10921092
`);
10931093
assert.fail();
10941094
} catch (e) {
1095-
assert.ok(e instanceof AiScriptSyntaxError);
1095+
assert.ok(e instanceof AiScriptRuntimeError);
10961096
}
10971097

10981098
try {

unreleased/reserved-word-keys.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- プロパティアクセスのドット記法に予約語を記述できるようになりました。
2+
- プロパティアクセスのドット記法に文字列リテラルを記述できるようになりました。

0 commit comments

Comments
 (0)