From a8c77694230374311f45e89de944fda020283d44 Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Thu, 18 Apr 2024 12:15:48 +0200 Subject: [PATCH 1/3] Move abstract-value-type into own file --- .../value-type/abstract-value-type.ts | 64 +++++++++++++++++++ .../wrappers/value-type/atomic-value-type.ts | 7 +- .../primitive/primitive-value-type.ts | 3 +- .../lib/ast/wrappers/value-type/value-type.ts | 60 ----------------- 4 files changed, 68 insertions(+), 66 deletions(-) create mode 100644 libs/language-server/src/lib/ast/wrappers/value-type/abstract-value-type.ts diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/abstract-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/abstract-value-type.ts new file mode 100644 index 000000000..0720e7b87 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/abstract-value-type.ts @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { type InternalValueRepresentation } from '../../expressions'; + +import { type ValueType, type ValueTypeVisitor } from './value-type'; + +export abstract class AbstractValueType + implements ValueType +{ + abstract acceptVisitor(visitor: ValueTypeVisitor): R; + + isSubtypeOf(other: ValueType): boolean { + let othersSupertype = other.getSupertype(); + while (othersSupertype !== undefined) { + if (othersSupertype === this) { + return true; + } + othersSupertype = othersSupertype.getSupertype(); + } + return false; + } + + getSupertype(): ValueType | undefined { + if (this.hasSupertypeCycle()) { + return undefined; + } + return this.doGetSupertype(); + } + + protected abstract doGetSupertype(): ValueType | undefined; + + abstract equals(target: ValueType): boolean; + + abstract isAllowedAsRuntimeParameter(): boolean; + + abstract isConvertibleTo(target: ValueType): boolean; + + isReferenceableByUser(): boolean { + return false; + } + + abstract isInternalValueRepresentation( + operandValue: InternalValueRepresentation | undefined, + ): operandValue is I; + + abstract getName(): string; + + hasSupertypeCycle(visited: ValueType[] = []): boolean { + const cycleDetected = visited.some((v) => v.equals(this)); + if (cycleDetected) { + return true; + } + visited.push(this); + + const supertype = this.doGetSupertype(); + if (supertype === undefined) { + return false; + } + + return supertype.hasSupertypeCycle(visited); + } +} diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/atomic-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/atomic-value-type.ts index 1c43c1357..49acf1aba 100644 --- a/libs/language-server/src/lib/ast/wrappers/value-type/atomic-value-type.ts +++ b/libs/language-server/src/lib/ast/wrappers/value-type/atomic-value-type.ts @@ -14,13 +14,10 @@ import { import { type AstNodeWrapper } from '../ast-node-wrapper'; import { type WrapperFactoryProvider } from '../wrapper-factory-provider'; +import { AbstractValueType } from './abstract-value-type'; import { type ValueTypeProvider } from './primitive'; import { CollectionValueType } from './primitive/collection/collection-value-type'; -import { - AbstractValueType, - type ValueType, - type ValueTypeVisitor, -} from './value-type'; +import { type ValueType, type ValueTypeVisitor } from './value-type'; export class AtomicValueType extends AbstractValueType diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/primitive-value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/primitive-value-type.ts index c2b171e22..654fd8b37 100644 --- a/libs/language-server/src/lib/ast/wrappers/value-type/primitive/primitive-value-type.ts +++ b/libs/language-server/src/lib/ast/wrappers/value-type/primitive/primitive-value-type.ts @@ -3,7 +3,8 @@ // SPDX-License-Identifier: AGPL-3.0-only import { type InternalValueRepresentation } from '../../../expressions/internal-value-representation'; -import { AbstractValueType, type ValueType } from '../value-type'; +import { AbstractValueType } from '../abstract-value-type'; +import { type ValueType } from '../value-type'; export abstract class PrimitiveValueType< I extends InternalValueRepresentation = InternalValueRepresentation, diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/value-type.ts b/libs/language-server/src/lib/ast/wrappers/value-type/value-type.ts index 3b964394c..348bae8a8 100644 --- a/libs/language-server/src/lib/ast/wrappers/value-type/value-type.ts +++ b/libs/language-server/src/lib/ast/wrappers/value-type/value-type.ts @@ -3,7 +3,6 @@ // SPDX-License-Identifier: AGPL-3.0-only import { type InternalValueRepresentation } from '../../expressions/internal-value-representation'; -import { type ValuetypeDefinition } from '../../generated/ast'; import { type AtomicValueType } from './atomic-value-type'; import { @@ -20,8 +19,6 @@ import { type ValuetypeAssignmentValuetype, } from './primitive'; -export type ValueTypeAstNode = ValuetypeDefinition; - export interface VisitableValueType { acceptVisitor(visitor: ValueTypeVisitor): void; } @@ -79,63 +76,6 @@ export interface ValueType< equals(target: ValueType): boolean; } -export abstract class AbstractValueType - implements ValueType -{ - abstract acceptVisitor(visitor: ValueTypeVisitor): R; - - isSubtypeOf(other: ValueType): boolean { - let othersSupertype = other.getSupertype(); - while (othersSupertype !== undefined) { - if (othersSupertype === this) { - return true; - } - othersSupertype = othersSupertype.getSupertype(); - } - return false; - } - - getSupertype(): ValueType | undefined { - if (this.hasSupertypeCycle()) { - return undefined; - } - return this.doGetSupertype(); - } - - protected abstract doGetSupertype(): ValueType | undefined; - - abstract equals(target: ValueType): boolean; - - abstract isAllowedAsRuntimeParameter(): boolean; - - abstract isConvertibleTo(target: ValueType): boolean; - - isReferenceableByUser(): boolean { - return false; - } - - abstract isInternalValueRepresentation( - operandValue: InternalValueRepresentation | undefined, - ): operandValue is I; - - abstract getName(): string; - - hasSupertypeCycle(visited: ValueType[] = []): boolean { - const cycleDetected = visited.some((v) => v.equals(this)); - if (cycleDetected) { - return true; - } - visited.push(this); - - const supertype = this.doGetSupertype(); - if (supertype === undefined) { - return false; - } - - return supertype.hasSupertypeCycle(visited); - } -} - export abstract class ValueTypeVisitor { abstract visitBoolean(valueType: BooleanValuetype): R; abstract visitDecimal(valueType: DecimalValuetype): R; From 436e6920d0e0e3d08a3242758f4156e58984f11a Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Thu, 18 Apr 2024 13:39:17 +0200 Subject: [PATCH 2/3] Move value type utility functions into own file to resolve dependency cycles --- .../src/lib/ast/expressions/index.ts | 1 - .../src/lib/ast/expressions/type-inference.ts | 91 +------------------ .../src/lib/ast/wrappers/index.ts | 1 - .../typed-object/block-type-wrapper.ts | 1 - .../composite-block-type-wrapper.ts | 1 - .../src/lib/ast/wrappers/value-type/index.ts | 2 + .../src/lib/ast/wrappers/value-type/util.ts | 91 +++++++++++++++++++ .../ast/wrappers/wrapper-factory-provider.ts | 1 - 8 files changed, 98 insertions(+), 91 deletions(-) create mode 100644 libs/language-server/src/lib/ast/wrappers/value-type/util.ts diff --git a/libs/language-server/src/lib/ast/expressions/index.ts b/libs/language-server/src/lib/ast/expressions/index.ts index 0ffbe65f1..3d1923dfc 100644 --- a/libs/language-server/src/lib/ast/expressions/index.ts +++ b/libs/language-server/src/lib/ast/expressions/index.ts @@ -7,7 +7,6 @@ export * from './evaluation-strategy'; export * from './evaluation-context'; export * from './internal-value-representation'; export * from './operator-registry'; -// eslint-disable-next-line import/no-cycle export * from './type-inference'; export * from './typeguards'; export * from './evaluate-expression'; diff --git a/libs/language-server/src/lib/ast/expressions/type-inference.ts b/libs/language-server/src/lib/ast/expressions/type-inference.ts index 7e699ac1b..2ca1c37df 100644 --- a/libs/language-server/src/lib/ast/expressions/type-inference.ts +++ b/libs/language-server/src/lib/ast/expressions/type-inference.ts @@ -2,8 +2,6 @@ // // SPDX-License-Identifier: AGPL-3.0-only -import { strict as assert } from 'assert'; - import { assertUnreachable } from 'langium'; import { type ValidationContext } from '../../validation/validation-context'; @@ -36,16 +34,16 @@ import { isValuetypeAssignmentLiteral, } from '../generated/ast'; import { getNextAstNodeContainer } from '../model-util'; -// eslint-disable-next-line import/no-cycle import { - type AtomicValueType, - type PrimitiveValueType, type ValueType, type ValueTypeProvider, type WrapperFactoryProvider, - isAtomicValueType, - isPrimitiveValueType, } from '../wrappers'; +import { + getValuetypeHierarchyStack, + pickCommonAtomicValueType, + pickCommonPrimitiveValuetype, +} from '../wrappers/value-type/util'; import { isEveryValueDefined } from './typeguards'; @@ -289,85 +287,6 @@ function inferCollectionElementTypes( return elementValuetypes; } -type ValuetypeHierarchyStack = [PrimitiveValueType, ...AtomicValueType[]]; - -function getValuetypeHierarchyStack( - valueType: ValueType, -): ValuetypeHierarchyStack { - if (isPrimitiveValueType(valueType)) { - return [valueType]; - } else if (isAtomicValueType(valueType)) { - const supertype = valueType.getSupertype(); - assert(supertype !== undefined); - return [...getValuetypeHierarchyStack(supertype), valueType]; - } - throw new Error( - 'Should be unreachable, encountered an unknown kind of value type', - ); -} - -function pickCommonPrimitiveValuetype( - primitiveValuetypes: PrimitiveValueType[], -): PrimitiveValueType | undefined { - assert(primitiveValuetypes.length > 0); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - let resultingType: PrimitiveValueType = primitiveValuetypes[0]!; - for (let i = 1; i < primitiveValuetypes.length; ++i) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const currentType = primitiveValuetypes[i]!; - - if (currentType.isConvertibleTo(resultingType)) { - continue; - } - - if (resultingType.isConvertibleTo(currentType)) { - // Pick the more general type as a result - resultingType = currentType; - continue; - } - - // Unable to convert the value types into each other, so there is no common primitive value type - return undefined; - } - return resultingType; -} - -function pickCommonAtomicValueType( - stacks: ValuetypeHierarchyStack[], -): PrimitiveValueType | AtomicValueType | undefined { - const minimumStackLength = Math.min(...stacks.map((stack) => stack.length)); - - let resultingType: PrimitiveValueType | AtomicValueType | undefined = - undefined; - for (let stackLevel = 1; stackLevel < minimumStackLength; ++stackLevel) { - const typesOfCurrentLevel: (PrimitiveValueType | AtomicValueType)[] = - stacks.map( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - (stack) => stack[stackLevel]!, - ); - - if (!areAllTypesEqual(typesOfCurrentLevel)) { - // Return the common value type of the previous level - return resultingType; - } - - // Pick any type of the current level since they are all equal - resultingType = typesOfCurrentLevel[0]; - } - return resultingType; -} - -function areAllTypesEqual(types: ValueType[]): boolean { - for (let i = 1; i < types.length; i++) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - if (!types[i - 1]!.equals(types[i]!)) { - return false; - } - } - - return true; -} - function inferTypeFromValueKeyword( expression: ValueKeywordLiteral, validationContext: ValidationContext, diff --git a/libs/language-server/src/lib/ast/wrappers/index.ts b/libs/language-server/src/lib/ast/wrappers/index.ts index 0c12ce60d..5bb4b5e3e 100644 --- a/libs/language-server/src/lib/ast/wrappers/index.ts +++ b/libs/language-server/src/lib/ast/wrappers/index.ts @@ -17,7 +17,6 @@ export { export { type PipeWrapper } from './pipe-wrapper'; export { type PipelineWrapper } from './pipeline-wrapper'; -// eslint-disable-next-line import/no-cycle export { type BlockTypeWrapper } from './typed-object/block-type-wrapper'; export { type CompositeBlockTypeWrapper } from './typed-object/composite-block-type-wrapper'; export { type ConstraintTypeWrapper } from './typed-object/constrainttype-wrapper'; diff --git a/libs/language-server/src/lib/ast/wrappers/typed-object/block-type-wrapper.ts b/libs/language-server/src/lib/ast/wrappers/typed-object/block-type-wrapper.ts index 3e29090b2..8beaf78ae 100644 --- a/libs/language-server/src/lib/ast/wrappers/typed-object/block-type-wrapper.ts +++ b/libs/language-server/src/lib/ast/wrappers/typed-object/block-type-wrapper.ts @@ -7,7 +7,6 @@ import { strict as assert } from 'assert'; import { type Reference, isReference } from 'langium'; import { RuntimeParameterProvider } from '../../../services'; -// eslint-disable-next-line import/no-cycle import { EvaluationContext, type OperatorEvaluatorRegistry, diff --git a/libs/language-server/src/lib/ast/wrappers/typed-object/composite-block-type-wrapper.ts b/libs/language-server/src/lib/ast/wrappers/typed-object/composite-block-type-wrapper.ts index bb2b5d74c..3667f39c4 100644 --- a/libs/language-server/src/lib/ast/wrappers/typed-object/composite-block-type-wrapper.ts +++ b/libs/language-server/src/lib/ast/wrappers/typed-object/composite-block-type-wrapper.ts @@ -7,7 +7,6 @@ import { type CompositeBlockTypeDefinition } from '../../generated/ast'; import { type ValueTypeProvider } from '../value-type'; import { type WrapperFactoryProvider } from '../wrapper-factory-provider'; -// eslint-disable-next-line import/no-cycle import { BlockTypeWrapper } from './block-type-wrapper'; export class CompositeBlockTypeWrapper extends BlockTypeWrapper { diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/index.ts b/libs/language-server/src/lib/ast/wrappers/value-type/index.ts index d5f70003b..0b5cb0dba 100644 --- a/libs/language-server/src/lib/ast/wrappers/value-type/index.ts +++ b/libs/language-server/src/lib/ast/wrappers/value-type/index.ts @@ -2,6 +2,8 @@ // // SPDX-License-Identifier: AGPL-3.0-only +export * from './util'; + /* * Note: Only export types if possible to enforce usage of WrapperFactory outside this directory. * This allows us to avoid dependency cycles between the language server and interpreter. diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/util.ts b/libs/language-server/src/lib/ast/wrappers/value-type/util.ts new file mode 100644 index 000000000..da32a5871 --- /dev/null +++ b/libs/language-server/src/lib/ast/wrappers/value-type/util.ts @@ -0,0 +1,91 @@ +// SPDX-FileCopyrightText: 2023 Friedrich-Alexander-Universitat Erlangen-Nurnberg +// +// SPDX-License-Identifier: AGPL-3.0-only + +import { strict as assert } from 'assert'; + +import { type AtomicValueType, isAtomicValueType } from './atomic-value-type'; +import { type PrimitiveValueType, isPrimitiveValueType } from './primitive'; +import { type ValueType } from './value-type'; + +type ValuetypeHierarchyStack = [PrimitiveValueType, ...AtomicValueType[]]; + +export function getValuetypeHierarchyStack( + valueType: ValueType, +): ValuetypeHierarchyStack { + if (isPrimitiveValueType(valueType)) { + return [valueType]; + } else if (isAtomicValueType(valueType)) { + const supertype = valueType.getSupertype(); + assert(supertype !== undefined); + return [...getValuetypeHierarchyStack(supertype), valueType]; + } + throw new Error( + 'Should be unreachable, encountered an unknown kind of value type', + ); +} + +export function pickCommonPrimitiveValuetype( + primitiveValuetypes: PrimitiveValueType[], +): PrimitiveValueType | undefined { + assert(primitiveValuetypes.length > 0); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + let resultingType: PrimitiveValueType = primitiveValuetypes[0]!; + for (let i = 1; i < primitiveValuetypes.length; ++i) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const currentType = primitiveValuetypes[i]!; + + if (currentType.isConvertibleTo(resultingType)) { + continue; + } + + if (resultingType.isConvertibleTo(currentType)) { + // Pick the more general type as a result + resultingType = currentType; + continue; + } + + // Unable to convert the value types into each other, so there is no common primitive value type + return undefined; + } + return resultingType; +} + +export function pickCommonAtomicValueType( + stacks: ValuetypeHierarchyStack[], +): PrimitiveValueType | AtomicValueType | undefined { + const minimumStackLength = Math.min(...stacks.map((stack) => stack.length)); + + let resultingType: PrimitiveValueType | AtomicValueType | undefined = + undefined; + for (let stackLevel = 1; stackLevel < minimumStackLength; ++stackLevel) { + const typesOfCurrentLevel: (PrimitiveValueType | AtomicValueType)[] = + stacks.map( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + (stack) => stack[stackLevel]!, + ); + + if (!areAllTypesEqual(typesOfCurrentLevel)) { + // Return the common value type of the previous level + return resultingType; + } + + // Pick any type of the current level since they are all equal + resultingType = typesOfCurrentLevel[0]; + } + return resultingType; +} + +export function areAllTypesEqual(types: ValueType[]): boolean { + for (let i = 1; i < types.length; i++) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const current = types[i - 1]!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const afterCurrent = types[i]!; + if (!current.equals(afterCurrent)) { + return false; + } + } + + return true; +} diff --git a/libs/language-server/src/lib/ast/wrappers/wrapper-factory-provider.ts b/libs/language-server/src/lib/ast/wrappers/wrapper-factory-provider.ts index 3dcf6457c..de553fbee 100644 --- a/libs/language-server/src/lib/ast/wrappers/wrapper-factory-provider.ts +++ b/libs/language-server/src/lib/ast/wrappers/wrapper-factory-provider.ts @@ -32,7 +32,6 @@ import { type AstNodeWrapper } from './ast-node-wrapper'; import { CellRangeWrapper } from './cell-range-wrapper'; import { PipeWrapper } from './pipe-wrapper'; import { PipelineWrapper } from './pipeline-wrapper'; -// eslint-disable-next-line import/no-cycle import { BlockTypeWrapper } from './typed-object/block-type-wrapper'; import { ConstraintTypeWrapper } from './typed-object/constrainttype-wrapper'; import { type PrimitiveValueType, type ValueType } from './value-type'; From 9ae26707622f359b862f8440e13a37802a21399a Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Thu, 18 Apr 2024 18:49:04 +0200 Subject: [PATCH 3/3] Move value type utils to wrapper/utils directory --- .../src/lib/ast/expressions/type-inference.ts | 2 +- libs/language-server/src/lib/ast/wrappers/index.ts | 13 +++++++++---- .../src/lib/ast/wrappers/util/index.ts | 1 + .../{value-type/util.ts => util/value-type-util.ts} | 12 +++++++++--- .../src/lib/ast/wrappers/value-type/index.ts | 2 -- 5 files changed, 20 insertions(+), 10 deletions(-) rename libs/language-server/src/lib/ast/wrappers/{value-type/util.ts => util/value-type-util.ts} (92%) diff --git a/libs/language-server/src/lib/ast/expressions/type-inference.ts b/libs/language-server/src/lib/ast/expressions/type-inference.ts index 2ca1c37df..6580a5a41 100644 --- a/libs/language-server/src/lib/ast/expressions/type-inference.ts +++ b/libs/language-server/src/lib/ast/expressions/type-inference.ts @@ -43,7 +43,7 @@ import { getValuetypeHierarchyStack, pickCommonAtomicValueType, pickCommonPrimitiveValuetype, -} from '../wrappers/value-type/util'; +} from '../wrappers/util/value-type-util'; import { isEveryValueDefined } from './typeguards'; diff --git a/libs/language-server/src/lib/ast/wrappers/index.ts b/libs/language-server/src/lib/ast/wrappers/index.ts index 5bb4b5e3e..3dc2d8dfc 100644 --- a/libs/language-server/src/lib/ast/wrappers/index.ts +++ b/libs/language-server/src/lib/ast/wrappers/index.ts @@ -2,6 +2,9 @@ // // SPDX-License-Identifier: AGPL-3.0-only +export * from './wrapper-factory-provider'; +export * from './util'; + /* * Note: Only export types if possible to enforce usage of WrapperFactory outside this directory. * This allows us to avoid dependency cycles between the language server and interpreter. @@ -20,9 +23,11 @@ export { type PipelineWrapper } from './pipeline-wrapper'; export { type BlockTypeWrapper } from './typed-object/block-type-wrapper'; export { type CompositeBlockTypeWrapper } from './typed-object/composite-block-type-wrapper'; export { type ConstraintTypeWrapper } from './typed-object/constrainttype-wrapper'; -export * from './typed-object/typed-object-wrapper'; +export { + ExampleDoc, + PropertyDocs, + PropertySpecification, + type TypedObjectWrapper, +} from './typed-object/typed-object-wrapper'; export * from './value-type'; // type export handled one level deeper - -export * from './util'; -export * from './wrapper-factory-provider'; diff --git a/libs/language-server/src/lib/ast/wrappers/util/index.ts b/libs/language-server/src/lib/ast/wrappers/util/index.ts index 6bf02875c..e703e85df 100644 --- a/libs/language-server/src/lib/ast/wrappers/util/index.ts +++ b/libs/language-server/src/lib/ast/wrappers/util/index.ts @@ -5,3 +5,4 @@ export * from './column-id-util'; export * from './cell-index'; export * from './cell-range-util'; +export * from './value-type-util'; diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/util.ts b/libs/language-server/src/lib/ast/wrappers/util/value-type-util.ts similarity index 92% rename from libs/language-server/src/lib/ast/wrappers/value-type/util.ts rename to libs/language-server/src/lib/ast/wrappers/util/value-type-util.ts index da32a5871..69be6e8a2 100644 --- a/libs/language-server/src/lib/ast/wrappers/value-type/util.ts +++ b/libs/language-server/src/lib/ast/wrappers/util/value-type-util.ts @@ -4,9 +4,15 @@ import { strict as assert } from 'assert'; -import { type AtomicValueType, isAtomicValueType } from './atomic-value-type'; -import { type PrimitiveValueType, isPrimitiveValueType } from './primitive'; -import { type ValueType } from './value-type'; +import { + type AtomicValueType, + isAtomicValueType, +} from '../value-type/atomic-value-type'; +import { + type PrimitiveValueType, + isPrimitiveValueType, +} from '../value-type/primitive'; +import { type ValueType } from '../value-type/value-type'; type ValuetypeHierarchyStack = [PrimitiveValueType, ...AtomicValueType[]]; diff --git a/libs/language-server/src/lib/ast/wrappers/value-type/index.ts b/libs/language-server/src/lib/ast/wrappers/value-type/index.ts index 0b5cb0dba..d5f70003b 100644 --- a/libs/language-server/src/lib/ast/wrappers/value-type/index.ts +++ b/libs/language-server/src/lib/ast/wrappers/value-type/index.ts @@ -2,8 +2,6 @@ // // SPDX-License-Identifier: AGPL-3.0-only -export * from './util'; - /* * Note: Only export types if possible to enforce usage of WrapperFactory outside this directory. * This allows us to avoid dependency cycles between the language server and interpreter.