Skip to content

Commit b39ac83

Browse files
authored
feat: オブジェクトリテラルで省略記法を使用可能にする (#1023)
* オブジェクトリテラル内で省略記法に対応 * fix: isStaticが真ならショートハンド構文を無効化 * fix: キーがIdentifierなときにのみ使用可能にする * fix lint
1 parent d8d9c09 commit b39ac83

File tree

5 files changed

+46
-4
lines changed

5 files changed

+46
-4
lines changed

src/parser/syntaxes/expressions.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ function parseReference(s: ITokenStream): Ast.Identifier {
577577

578578
/**
579579
* ```abnf
580-
* Object = "{" [ObjectKey ":" Expr *(SEP IDENT ":" Expr) [SEP]] "}"
580+
* Object = "{" [(ObjectKey ":" Expr / IDENT) *(SEP (ObjectKey ":" Expr / IDENT)) [SEP]] "}"
581581
* ```
582582
*/
583583
function parseObject(s: ITokenStream, isStatic: boolean): Ast.Obj {
@@ -592,16 +592,25 @@ function parseObject(s: ITokenStream, isStatic: boolean): Ast.Obj {
592592

593593
const map = new Map<string, Ast.Expression>();
594594
while (!s.is(TokenKind.CloseBrace)) {
595+
const startPos = s.getPos();
596+
597+
const isIdentifierKey = s.is(TokenKind.Identifier);
595598
const k = parseObjectKey(s);
596599
if (map.has(k)) {
597600
throw new AiScriptSyntaxError(`Key ${k} is duplicated.`, s.getPos());
598601
}
599602
s.next();
600603

601-
s.expect(TokenKind.Colon);
602-
s.next();
604+
let v: Ast.Expression;
603605

604-
const v = parseExpr(s, isStatic);
606+
if (!isIdentifierKey || isStatic) s.expect(TokenKind.Colon);
607+
if (s.is(TokenKind.Colon)) {
608+
s.next();
609+
610+
v = parseExpr(s, isStatic);
611+
} else {
612+
v = NODE('identifier', { name: k }, startPos, s.getPos());
613+
}
605614

606615
map.set(k, v);
607616

test/aison.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ greet()`)).toThrow();
6262
expect(() => AiSON.parse('{key: (3 + 5)}')).toThrow();
6363
});
6464

65+
test.concurrent('not allowed: object shorthand', () => {
66+
expect(() => AiSON.parse('{key}')).toThrow();
67+
});
68+
6569
test.concurrent('not allowed: labeled expression', () => {
6670
expect(() => AiSON.parse('#label: eval { 1 }')).toThrow();
6771
});

test/literals.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,26 @@ describe('literal', () => {
243243
});
244244
});
245245

246+
test.concurrent('obj (shorthand)', async () => {
247+
const res = await exe(`
248+
let a = 1
249+
<: { a }
250+
`);
251+
eq(res, OBJ(new Map([['a', NUM(1)]])));
252+
});
253+
254+
test.concurrent('obj (reserved word as shorthand)', async () => {
255+
await expect(() => exe(`
256+
<: { exists }
257+
`)).rejects.toThrow(AiScriptSyntaxError);
258+
});
259+
260+
test.concurrent('obj (string as shorthand)', async () => {
261+
await expect(() => exe(`
262+
<: { "hoge" }
263+
`)).rejects.toThrow(AiScriptSyntaxError);
264+
});
265+
246266
test.concurrent('obj (escaped reserved word as key)', async () => {
247267
await expect(async () => await exe(`
248268
<: {

test/syntax.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,13 @@ describe('Variable declaration', () => {
635635
`);
636636
eq(res, ARR([NUM(1), NUM(2)]));
637637
});
638+
test.concurrent('destructuring declaration with shorthand', async () => {
639+
const res = await exe(`
640+
let { value } = { value: 1 }
641+
<: value
642+
`);
643+
eq(res, NUM(1));
644+
});
638645
test.concurrent('empty function', async () => {
639646
const res = await exe(`
640647
@hoge() { }
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- オブジェクトリテラルとオブジェクトの分割代入構文内で、キーと同名の変数を参照する省略記法を使用できるようになりました。
2+
- 例えば`{ hoge }`は`{ hoge: hoge }`と等価です。

0 commit comments

Comments
 (0)