Add basic parsing support for let and const

This commit is contained in:
Mohamed Hegazy 2014-10-12 21:10:04 -07:00
parent 873c1df74b
commit 778f101dea
28 changed files with 557 additions and 7 deletions

View File

@ -115,6 +115,10 @@ module ts {
Filename_0_differs_from_already_included_filename_1_only_in_casing: { code: 1149, category: DiagnosticCategory.Error, key: "Filename '{0}' differs from already included filename '{1}' only in casing" },
new_T_cannot_be_used_to_create_an_array_Use_new_Array_T_instead: { code: 1150, category: DiagnosticCategory.Error, key: "'new T[]' cannot be used to create an array. Use 'new Array<T>()' instead." },
An_enum_member_cannot_have_a_numeric_name: { code: 1151, category: DiagnosticCategory.Error, key: "An enum member cannot have a numeric name." },
var_let_or_const_expected: { code: 1152, category: DiagnosticCategory.Error, key: "'var', 'let' or 'const' expected." },
let_variable_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher: { code: 1153, category: DiagnosticCategory.Error, key: "'let' variable declarations are only available when targeting ECMAScript 6 and higher." },
const_variable_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher: { code: 1154, category: DiagnosticCategory.Error, key: "'const' variable declarations are only available when targeting ECMAScript 6 and higher." },
Const_must_be_intialized: { code: 1155, category: DiagnosticCategory.Error, key: "Const must be intialized." },
Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." },
Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." },
Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." },

View File

@ -451,6 +451,22 @@
"category": "Error",
"code": 1151
},
"'var', 'let' or 'const' expected.": {
"category": "Error",
"code": 1152
},
"'let' variable declarations are only available when targeting ECMAScript 6 and higher.": {
"category": "Error",
"code": 1153
},
"'const' variable declarations are only available when targeting ECMAScript 6 and higher.": {
"category": "Error",
"code": 1154
},
"Const must be intialized.": {
"category": "Error",
"code": 1155
},
"Duplicate identifier '{0}'.": {
"category": "Error",

View File

@ -1149,7 +1149,12 @@ module ts {
write(" ");
endPos = emitToken(SyntaxKind.OpenParenToken, endPos);
if (node.declarations) {
emitToken(SyntaxKind.VarKeyword, endPos);
if (node.declarations[0] && node.declarations[0].flags & NodeFlags.Let) {
emitToken(SyntaxKind.LetKeyword, endPos);
}
else {
emitToken(SyntaxKind.VarKeyword, endPos);
}
write(" ");
emitCommaList(node.declarations, /*includeTrailingComma*/ false);
}
@ -1169,7 +1174,12 @@ module ts {
write(" ");
endPos = emitToken(SyntaxKind.OpenParenToken, endPos);
if (node.declaration) {
emitToken(SyntaxKind.VarKeyword, endPos);
if (node.declaration.flags & NodeFlags.Let) {
emitToken(SyntaxKind.LetKeyword, endPos);
}
else {
emitToken(SyntaxKind.VarKeyword, endPos);
}
write(" ");
emit(node.declaration);
}
@ -1298,7 +1308,17 @@ module ts {
function emitVariableStatement(node: VariableStatement) {
emitLeadingComments(node);
if (!(node.flags & NodeFlags.Export)) write("var ");
if (!(node.flags & NodeFlags.Export)) {
if (node.flags & NodeFlags.Let) {
write("let ");
}
else if (node.flags & NodeFlags.Const) {
write("const ");
}
else {
write("var ");
}
}
emitCommaList(node.declarations, /*includeTrailingComma*/ false);
write(";");
emitTrailingComments(node);
@ -1774,7 +1794,15 @@ module ts {
if (node.flags & NodeFlags.Export) {
writeLine();
emitStart(node);
write("var ");
if (node.flags & NodeFlags.Let) {
write("let ");
}
else if (node.flags & NodeFlags.Const) {
write("const ");
}
else {
write("var ");
}
emit(node.name);
write(" = ");
emitModuleMemberName(node);
@ -2822,7 +2850,15 @@ module ts {
if (hasDeclarationWithEmit) {
emitJsDocComments(node);
emitDeclarationFlags(node);
write("var ");
if (node.flags & NodeFlags.Let) {
write("let ");
}
else if (node.flags & NodeFlags.Const) {
write("const ");
}
else {
write("var ");
}
emitCommaList(node.declarations, emitVariableDeclaration);
write(";");
writeLine();

View File

@ -2645,6 +2645,15 @@ module ts {
error(Diagnostics.Variable_declaration_list_cannot_be_empty);
}
}
else if (parseOptional(SyntaxKind.LetKeyword)) {
var declarations = parseVariableDeclarationList(NodeFlags.Let, true);
if (!declarations.length) {
error(Diagnostics.Variable_declaration_list_cannot_be_empty);
}
if (languageVersion < ScriptTarget.ES6) {
grammarErrorAtPos(declarations.pos, declarations.end - declarations.pos, Diagnostics.let_variable_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher);
}
}
else {
var varOrInit = parseExpression(true);
}
@ -2970,6 +2979,8 @@ module ts {
return !inErrorRecovery;
case SyntaxKind.OpenBraceToken:
case SyntaxKind.VarKeyword:
case SyntaxKind.LetKeyword:
case SyntaxKind.ConstKeyword:
case SyntaxKind.FunctionKeyword:
case SyntaxKind.IfKeyword:
case SyntaxKind.DoKeyword:
@ -3016,6 +3027,8 @@ module ts {
case SyntaxKind.OpenBraceToken:
return parseBlock(/* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false);
case SyntaxKind.VarKeyword:
case SyntaxKind.LetKeyword:
case SyntaxKind.ConstKeyword:
return parseVariableStatement();
case SyntaxKind.FunctionKeyword:
return parseFunctionDeclaration();
@ -3095,6 +3108,9 @@ module ts {
if (inAmbientContext && node.initializer && errorCountBeforeVariableDeclaration === file.syntacticErrors.length) {
grammarErrorAtPos(initializerStart, initializerFirstTokenLength, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
}
if (!inAmbientContext && !node.initializer && flags & NodeFlags.Const) {
grammarErrorOnNode(node, Diagnostics.Const_must_be_intialized);
}
if (isInStrictMode && isEvalOrArgumentsIdentifier(node.name)) {
// It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code
// and its Identifier is eval or arguments
@ -3112,13 +3128,30 @@ module ts {
var node = <VariableStatement>createNode(SyntaxKind.VariableStatement, pos);
if (flags) node.flags = flags;
var errorCountBeforeVarStatement = file.syntacticErrors.length;
parseExpected(SyntaxKind.VarKeyword);
node.declarations = parseVariableDeclarationList(flags, /*noIn*/false);
if (token === SyntaxKind.LetKeyword) {
node.flags |= NodeFlags.Let;
}
else if (token === SyntaxKind.ConstKeyword) {
node.flags |= NodeFlags.Const;
}
else if (token !== SyntaxKind.VarKeyword) {
error(Diagnostics.var_let_or_const_expected);
}
nextToken();
node.declarations = parseVariableDeclarationList(node.flags, /*noIn*/false);
parseSemicolon();
finishNode(node);
if (!node.declarations.length && file.syntacticErrors.length === errorCountBeforeVarStatement) {
grammarErrorOnNode(node, Diagnostics.Variable_declaration_list_cannot_be_empty);
}
if (languageVersion < ScriptTarget.ES6) {
if (node.flags & NodeFlags.Let) {
grammarErrorOnNode(node, Diagnostics.let_variable_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher);
}
else if (node.flags & NodeFlags.Const) {
grammarErrorOnNode(node, Diagnostics.const_variable_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher);
}
}
return node;
}
@ -3697,6 +3730,8 @@ module ts {
function isDeclaration(): boolean {
switch (token) {
case SyntaxKind.VarKeyword:
case SyntaxKind.LetKeyword:
case SyntaxKind.ConstKeyword:
case SyntaxKind.FunctionKeyword:
return true;
case SyntaxKind.ClassKeyword:
@ -3747,6 +3782,8 @@ module ts {
var result: Declaration;
switch (token) {
case SyntaxKind.VarKeyword:
case SyntaxKind.LetKeyword:
case SyntaxKind.ConstKeyword:
result = parseVariableStatement(pos, flags);
break;
case SyntaxKind.FunctionKeyword:

View File

@ -245,6 +245,8 @@ module ts {
MultiLine = 0x00000100, // Multi-line array or object literal
Synthetic = 0x00000200, // Synthetic node (for full fidelity)
DeclarationFile = 0x00000400, // Node is a .d.ts file
Let = 0x00000800,
Const = 0x00001000,
Modifier = Export | Ambient | Public | Private | Protected | Static,
AccessibilityModifier = Public | Private | Protected

View File

@ -0,0 +1,34 @@
tests/cases/compiler/constDeclarations-ambient-errors.ts(3,27): error TS1039: Initializers are not allowed in ambient contexts.
tests/cases/compiler/constDeclarations-ambient-errors.ts(4,26): error TS1039: Initializers are not allowed in ambient contexts.
tests/cases/compiler/constDeclarations-ambient-errors.ts(5,18): error TS1039: Initializers are not allowed in ambient contexts.
tests/cases/compiler/constDeclarations-ambient-errors.ts(5,37): error TS1039: Initializers are not allowed in ambient contexts.
tests/cases/compiler/constDeclarations-ambient-errors.ts(5,51): error TS1039: Initializers are not allowed in ambient contexts.
tests/cases/compiler/constDeclarations-ambient-errors.ts(8,14): error TS1039: Initializers are not allowed in ambient contexts.
tests/cases/compiler/constDeclarations-ambient-errors.ts(9,22): error TS1039: Initializers are not allowed in ambient contexts.
==== tests/cases/compiler/constDeclarations-ambient-errors.ts (7 errors) ====
// error: no intialization expected in ambient declarations
declare const c1: boolean = true;
~
!!! error TS1039: Initializers are not allowed in ambient contexts.
declare const c2: number = 0;
~
!!! error TS1039: Initializers are not allowed in ambient contexts.
declare const c3 = null, c4 :string = "", c5: any = 0;
~
!!! error TS1039: Initializers are not allowed in ambient contexts.
~
!!! error TS1039: Initializers are not allowed in ambient contexts.
~
!!! error TS1039: Initializers are not allowed in ambient contexts.
declare module M {
const c6 = 0;
~
!!! error TS1039: Initializers are not allowed in ambient contexts.
const c7: number = 7;
~
!!! error TS1039: Initializers are not allowed in ambient contexts.
}

View File

@ -0,0 +1,13 @@
//// [constDeclarations-ambient.ts]
// No error
declare const c1: boolean;
declare const c2: number;
declare const c3, c4 :string, c5: any;
declare module M {
const c6;
const c7: number;
}
//// [constDeclarations-ambient.js]

View File

@ -0,0 +1,23 @@
=== tests/cases/compiler/constDeclarations-ambient.ts ===
// No error
declare const c1: boolean;
>c1 : boolean
declare const c2: number;
>c2 : number
declare const c3, c4 :string, c5: any;
>c3 : any
>c4 : string
>c5 : any
declare module M {
>M : typeof M
const c6;
>c6 : any
const c7: number;
>c7 : number
}

View File

@ -0,0 +1,62 @@
tests/cases/compiler/constDeclarations-errors.ts(3,7): error TS1155: const must be intialized.
tests/cases/compiler/constDeclarations-errors.ts(4,7): error TS1155: const must be intialized.
tests/cases/compiler/constDeclarations-errors.ts(5,7): error TS1155: const must be intialized.
tests/cases/compiler/constDeclarations-errors.ts(5,11): error TS1155: const must be intialized.
tests/cases/compiler/constDeclarations-errors.ts(5,15): error TS1155: const must be intialized.
tests/cases/compiler/constDeclarations-errors.ts(5,27): error TS1155: const must be intialized.
tests/cases/compiler/constDeclarations-errors.ts(8,5): error TS1109: Expression expected.
tests/cases/compiler/constDeclarations-errors.ts(8,5): error TS1156: const must be declared inside a block.
tests/cases/compiler/constDeclarations-errors.ts(8,11): error TS1155: const must be intialized.
tests/cases/compiler/constDeclarations-errors.ts(8,13): error TS1005: ';' expected.
tests/cases/compiler/constDeclarations-errors.ts(8,13): error TS1128: Declaration or statement expected.
tests/cases/compiler/constDeclarations-errors.ts(8,18): error TS1128: Declaration or statement expected.
tests/cases/compiler/constDeclarations-errors.ts(10,5): error TS1109: Expression expected.
tests/cases/compiler/constDeclarations-errors.ts(10,5): error TS1156: const must be declared inside a block.
tests/cases/compiler/constDeclarations-errors.ts(10,28): error TS1005: ';' expected.
tests/cases/compiler/constDeclarations-errors.ts(10,11): error TS2403: Subsequent variable declarations must have the same type. Variable 'c' must be of type 'any', but here has type 'number'.
==== tests/cases/compiler/constDeclarations-errors.ts (16 errors) ====
// error, missing intialicer
const c1;
~~
!!! error TS1155: const must be intialized.
const c2: number;
~~
!!! error TS1155: const must be intialized.
const c3, c4, c5 :string, c6; // error, missing initialicer
~~
!!! error TS1155: const must be intialized.
~~
!!! error TS1155: const must be intialized.
~~
!!! error TS1155: const must be intialized.
~~
!!! error TS1155: const must be intialized.
// error, wrong context
for(const c in {}) { }
~~~~~
!!! error TS1109: Expression expected.
~~~~~~~
!!! error TS1156: const must be declared inside a block.
~
!!! error TS1155: const must be intialized.
~~
!!! error TS1005: ';' expected.
~~
!!! error TS1128: Declaration or statement expected.
~
!!! error TS1128: Declaration or statement expected.
for(const c = 0; c < 9; c++) { }
~~~~~
!!! error TS1109: Expression expected.
~~~~~~~~~~~~
!!! error TS1156: const must be declared inside a block.
~
!!! error TS1005: ';' expected.
~
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'c' must be of type 'any', but here has type 'number'.

View File

@ -0,0 +1,17 @@
tests/cases/compiler/constDeclarations-es5.ts(2,1): error TS1154: 'const' variable declarations are only available when targeting ECMAScript 6 and higher.
tests/cases/compiler/constDeclarations-es5.ts(3,1): error TS1154: 'const' variable declarations are only available when targeting ECMAScript 6 and higher.
tests/cases/compiler/constDeclarations-es5.ts(4,1): error TS1154: 'const' variable declarations are only available when targeting ECMAScript 6 and higher.
==== tests/cases/compiler/constDeclarations-es5.ts (3 errors) ====
const z7 = false;
~~~~~~~~~~~~~~~~~
!!! error TS1154: 'const' variable declarations are only available when targeting ECMAScript 6 and higher.
const z8: number = 23;
~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1154: 'const' variable declarations are only available when targeting ECMAScript 6 and higher.
const z9 = 0, z10 :string = "", z11 = null;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1154: 'const' variable declarations are only available when targeting ECMAScript 6 and higher.

View File

@ -0,0 +1,19 @@
//// [constDeclarations.ts]
// No error
const c1 = false;
const c2: number = 23;
const c3 = 0, c4 :string = "", c5 = null;
//// [constDeclarations.js]
// No error
const c1 = false;
const c2 = 23;
const c3 = 0, c4 = "", c5 = null;
//// [constDeclarations.d.ts]
declare const c1: boolean;
declare const c2: number;
declare const c3: number, c4: string, c5: any;

View File

@ -0,0 +1,14 @@
=== tests/cases/compiler/constDeclarations.ts ===
// No error
const c1 = false;
>c1 : boolean
const c2: number = 23;
>c2 : number
const c3 = 0, c4 :string = "", c5 = null;
>c3 : number
>c4 : string
>c5 : any

View File

@ -0,0 +1,26 @@
//// [constDeclarations2.ts]
// No error
module M {
export const c1 = false;
export const c2: number = 23;
export const c3 = 0, c4 :string = "", c5 = null;
}
//// [constDeclarations2.js]
// No error
var M;
(function (M) {
M.c1 = false;
M.c2 = 23;
M.c3 = 0, M.c4 = "", M.c5 = null;
})(M || (M = {}));
//// [constDeclarations2.d.ts]
declare module M {
const c1: boolean;
const c2: number;
const c3: number, c4: string, c5: any;
}

View File

@ -0,0 +1,18 @@
=== tests/cases/compiler/constDeclarations2.ts ===
// No error
module M {
>M : typeof M
export const c1 = false;
>c1 : boolean
export const c2: number = 23;
>c2 : number
export const c3 = 0, c4 :string = "", c5 = null;
>c3 : number
>c4 : string
>c5 : any
}

View File

@ -0,0 +1,40 @@
tests/cases/compiler/letDeclarations-es5.ts(2,1): error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.
tests/cases/compiler/letDeclarations-es5.ts(3,1): error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.
tests/cases/compiler/letDeclarations-es5.ts(4,1): error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.
tests/cases/compiler/letDeclarations-es5.ts(6,1): error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.
tests/cases/compiler/letDeclarations-es5.ts(7,1): error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.
tests/cases/compiler/letDeclarations-es5.ts(8,1): error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.
tests/cases/compiler/letDeclarations-es5.ts(10,8): error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.
tests/cases/compiler/letDeclarations-es5.ts(12,8): error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.
==== tests/cases/compiler/letDeclarations-es5.ts (8 errors) ====
let l1;
~~~~~~~
!!! error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.
let l2: number;
~~~~~~~~~~~~~~~
!!! error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.
let l3, l4, l5 :string, l6;
~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.
let l7 = false;
~~~~~~~~~~~~~~~
!!! error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.
let l8: number = 23;
~~~~~~~~~~~~~~~~~~~~
!!! error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.
let l9 = 0, l10 :string = "", l11 = null;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.
for(let l11 in {}) { }
~~~~
!!! error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.
for(let l12 = 0; l12 < 9; l12++) { }
~~~~~~~~
!!! error TS1153: 'let' variable declarations are only available when targeting ECMAScript 6 and higher.

View File

@ -0,0 +1,35 @@
//// [letDeclarations.ts]
let l1;
let l2: number;
let l3, l4, l5 :string, l6;
let l7 = false;
let l8: number = 23;
let l9 = 0, l10 :string = "", l11 = null;
for(let l11 in {}) { }
for(let l12 = 0; l12 < 9; l12++) { }
//// [letDeclarations.js]
let l1;
let l2;
let l3, l4, l5, l6;
let l7 = false;
let l8 = 23;
let l9 = 0, l10 = "", l11 = null;
for (let l11 in {}) {
}
for (let l12 = 0; l12 < 9; l12++) {
}
//// [letDeclarations.d.ts]
declare let l1: any;
declare let l2: number;
declare let l3: any, l4: any, l5: string, l6: any;
declare let l7: boolean;
declare let l8: number;
declare let l9: number, l10: string, l11: any;

View File

@ -0,0 +1,36 @@
=== tests/cases/compiler/letDeclarations.ts ===
let l1;
>l1 : any
let l2: number;
>l2 : number
let l3, l4, l5 :string, l6;
>l3 : any
>l4 : any
>l5 : string
>l6 : any
let l7 = false;
>l7 : boolean
let l8: number = 23;
>l8 : number
let l9 = 0, l10 :string = "", l11 = null;
>l9 : number
>l10 : string
>l11 : any
for(let l11 in {}) { }
>l11 : any
>{} : {}
for(let l12 = 0; l12 < 9; l12++) { }
>l12 : number
>l12 < 9 : boolean
>l12 : number
>l12++ : number
>l12 : number

View File

@ -0,0 +1,19 @@
//// [letDeclarations2.ts]
module M {
let l1 = "s";
export let l2 = 0;
}
//// [letDeclarations2.js]
var M;
(function (M) {
let l1 = "s";
M.l2 = 0;
})(M || (M = {}));
//// [letDeclarations2.d.ts]
declare module M {
let l2: number;
}

View File

@ -0,0 +1,11 @@
=== tests/cases/compiler/letDeclarations2.ts ===
module M {
>M : typeof M
let l1 = "s";
>l1 : string
export let l2 = 0;
>l2 : number
}

View File

@ -0,0 +1,11 @@
// @target: ES6
// error: no intialization expected in ambient declarations
declare const c1: boolean = true;
declare const c2: number = 0;
declare const c3 = null, c4 :string = "", c5: any = 0;
declare module M {
const c6 = 0;
const c7: number = 7;
}

View File

@ -0,0 +1,11 @@
// @target: ES6
// No error
declare const c1: boolean;
declare const c2: number;
declare const c3, c4 :string, c5: any;
declare module M {
const c6;
const c7: number;
}

View File

@ -0,0 +1,11 @@
// @target: ES6
// error, missing intialicer
const c1;
const c2: number;
const c3, c4, c5 :string, c6; // error, missing initialicer
// error, wrong context
for(const c in {}) { }
for(const c = 0; c < 9; c++) { }

View File

@ -0,0 +1,5 @@
// @target: ES5
const z7 = false;
const z8: number = 23;
const z9 = 0, z10 :string = "", z11 = null;

View File

@ -0,0 +1,7 @@
// @target: ES6
// @declaration: true
// No error
const c1 = false;
const c2: number = 23;
const c3 = 0, c4 :string = "", c5 = null;

View File

@ -0,0 +1,9 @@
// @target: ES6
// @declaration: true
// No error
module M {
export const c1 = false;
export const c2: number = 23;
export const c3 = 0, c4 :string = "", c5 = null;
}

View File

@ -0,0 +1,13 @@
// @target: ES5
let l1;
let l2: number;
let l3, l4, l5 :string, l6;
let l7 = false;
let l8: number = 23;
let l9 = 0, l10 :string = "", l11 = null;
for(let l11 in {}) { }
for(let l12 = 0; l12 < 9; l12++) { }

View File

@ -0,0 +1,14 @@
// @target: ES6
// @declaration: true
let l1;
let l2: number;
let l3, l4, l5 :string, l6;
let l7 = false;
let l8: number = 23;
let l9 = 0, l10 :string = "", l11 = null;
for(let l11 in {}) { }
for(let l12 = 0; l12 < 9; l12++) { }

View File

@ -0,0 +1,7 @@
// @target: ES6
// @declaration: true
module M {
let l1 = "s";
export let l2 = 0;
}