Commit 5d3cf7bb authored by Malte Heithoff's avatar Malte Heithoff
Browse files

Fix Ports and Parameters

parent c01bc12a
......@@ -64,13 +64,24 @@ grammar EmbeddedMontiArc extends de.monticore.lang.monticar.Common2 {
*/
symbol scope Component implements Element =
"component" (["interface"])? Name
//("component" | ["component_interface"]) Name
ComponentModifier* "component" (["interface"])? Name
genericTypeParameters:TypeParameters2?
( "(" (Parameter || ",")+ ")" )?
("implements" superComponent:ReferenceType)?
body:ComponentBody;
interface ComponentModifier;
VirtModifier implements ComponentModifier =
VIRTUAL;
enum VIRTUAL =
"virtual" | VIRTUAL:"virt" | "nonvirtual" | NONVIRTUAL:"non-virt";
DFModifier implements ComponentModifier =
DIRECTFEEDTHROUGH;
enum DIRECTFEEDTHROUGH =
DF:"direct feedthrough" | "df" | NONDF:"nondirect feedthrough" | NONDF:"non-df";
/**
* The body contains architectural elements of
......@@ -123,10 +134,10 @@ grammar EmbeddedMontiArc extends de.monticore.lang.monticar.Common2 {
* created
*/
SubComponent implements Element =
"instance"
"instance"
type:ReferenceType
("(" arguments:(Expression || ",")+ ")" )?
("{" (PortInitialValueOrGuess || ",")+ "}")?
("{" (PortInitial || ",")+ "}")?
instances:(SubComponentInstance || ",")+ ";" ;
......@@ -146,8 +157,11 @@ grammar EmbeddedMontiArc extends de.monticore.lang.monticar.Common2 {
Name
("[" UnitNumberResolution "]")?;
PortInitialValueOrGuess =
Name ( "[" UnitNumberResolution "]" )? (guess:["~"])? "=" Expression;
PortInitial =
Name ( "[" UnitNumberResolution "]" )?
"(" "t=0" ")"
(guess:["~"])? "="
Expression;
/**
* port1
......
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.lang.embeddedmontiarc.cocos;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.ASTComponent;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.ASTComponentModifier;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._cocos.EmbeddedMontiArcASTComponentCoCo;
import de.se_rwth.commons.logging.Log;
import java.util.HashSet;
import java.util.Set;
/**
* ensures that component modifiers such as virtual or nondirectfeedthrough are unique
*/
public class ComponentModifierUnique implements EmbeddedMontiArcASTComponentCoCo {
@Override
public void check(ASTComponent node) {
Set<Class> modifiers = new HashSet<>();
for (ASTComponentModifier astComponentModifier : node.getComponentModifierList()) {
if (modifiers.contains(astComponentModifier.getClass()))
Log.error("0xAC010 Component Modifiers must be unique");
modifiers.add(astComponentModifier.getClass());
}
}
}
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.lang.embeddedmontiarc.cocos;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.ASTPortInitialValueOrGuess;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.ASTPortInitial;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.ASTSubComponent;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._cocos.EmbeddedMontiArcASTSubComponentCoCo;
import de.monticore.mcexpressions._ast.ASTAssignmentExpression;
......@@ -10,8 +10,8 @@ import de.se_rwth.commons.logging.Log;
public class InitialGuessIsNotAssignmentCoCo implements EmbeddedMontiArcASTSubComponentCoCo {
@Override
public void check(ASTSubComponent node) {
for (ASTPortInitialValueOrGuess initialGuess : node.getPortInitialValueOrGuessList()) {
if (initialGuess.getExpression() instanceof ASTAssignmentExpression) {
for (ASTPortInitial portInitial : node.getPortInitialList()) {
if (portInitial.getExpression() instanceof ASTAssignmentExpression) {
Log.error(String.format("0x079B7 Initial guess of has to be an assignment"),
node.get_SourcePositionStart());
}
......
......@@ -16,12 +16,12 @@ import java.util.stream.Collectors;
*/
public class ASTComponent extends ASTComponentTOP {
public ASTComponent() {
protected ASTComponent() {
super();
}
public ASTComponent(String name, Optional<ASTTypeParameters2> genericTypeParameters, List<ASTParameter> parameters, Optional<ASTReferenceType> superComponent, ASTComponentBody body, boolean r__interface) {
super(name, genericTypeParameters, parameters, superComponent, body, r__interface);
protected ASTComponent(List<ASTComponentModifier> componentModifiers, String name, Optional<ASTTypeParameters2> genericTypeParameters, List<ASTParameter> parameters, Optional<ASTReferenceType> superComponent, ASTComponentBody body, boolean r__interface) {
super(componentModifiers, name, genericTypeParameters, parameters, superComponent, body, r__interface);
}
// do not use symbol table, since symbol table must not be created
......
......@@ -103,7 +103,7 @@ public class EmbeddedMontiArcModelLoader extends EmbeddedMontiArcModelLoaderTOP
de.monticore.lang.monticar.Utils.addBuiltInTypes(scope);
LogConfig.init();
// LogConfig.init();
InstancingRegister.reset();
InstancingRegister.mainComponent = mainComponent;
InstancingRegister.mainInstantiation = mainInstantiation;
......
......@@ -174,6 +174,10 @@ public class EmbeddedMontiArcSymbolTableCreator extends EmbeddedMontiArcSymbolTa
component.setSuperComponent(Optional.of(ref));
}
// Component Modifiers
if (!node.getComponentModifierList().isEmpty())
component.setComponentModifiers(node.getComponentModifierList());
// check if this component is an inner component
if (!componentStack.isEmpty()) {
component.setIsInnerComponent(true);
......@@ -255,8 +259,9 @@ public class EmbeddedMontiArcSymbolTableCreator extends EmbeddedMontiArcSymbolTa
* configArgs.add(new ValueSymbol<>(value, Kind.Expression)); } */
componentTypeReference.setArguments(node.getArgumentsList());
componentTypeReference.fixResolutions(this);
// InitialGuesses
componentTypeReference.setInitialGuesses(node.getPortInitialValueOrGuessList());
// PortInitials
componentTypeReference.setPortInitials(node.getPortInitialList());
// instances
......
......@@ -3,7 +3,7 @@ package de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.cncMode
import com.google.common.collect.ImmutableList;
import de.monticore.expressionsbasis._ast.ASTExpression;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.ASTPortInitialValueOrGuess;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.*;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.ComponentKind;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanceStructure.EMAComponentInstantiationSymbol;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc.types.EMAVariable;
......@@ -42,8 +42,6 @@ public class EMAComponentSymbol extends CommonScopeSpanningSymbol implements EMA
private Optional<EMAComponentSymbolReference> superComponent = Optional.empty();
private boolean delayed = false;
// when "this" not actually is a component, but a reference to a component, this optional
// attribute is set by the symbol-table creator to the referenced component and must be used for
// implementation.
......@@ -57,7 +55,9 @@ public class EMAComponentSymbol extends CommonScopeSpanningSymbol implements EMA
private List<ASTExpression> arguments = new ArrayList<>();
private List<ASTPortInitialValueOrGuess> initalGuesses = new ArrayList<>();
private List<ASTPortInitial> portInitials = new ArrayList<>();
private List<ASTComponentModifier> componentModifiers = new ArrayList<>();
public EMAComponentSymbol(String name) {
super(name, KIND);
......@@ -518,22 +518,6 @@ public class EMAComponentSymbol extends CommonScopeSpanningSymbol implements EMA
.collect(Collectors.toList());
}
/**
* Sets, if the component has a delay.
*
* @param delayed true, if the component has a delay, else false.
*/
public void setDelayed(boolean delayed) {
referencedComponent.orElse(this).delayed = delayed;
}
/**
* @return true, if the component has a delay, else false.
*/
public boolean hasDelay() {
return referencedComponent.orElse(this).delayed;
}
public boolean isDecomposed() {
return !isAtomic();
}
......@@ -650,20 +634,70 @@ public class EMAComponentSymbol extends CommonScopeSpanningSymbol implements EMA
return (Optional<EMAComponentSymbol>) getEnclosingScope().getSpanningSymbol();
}
public List<ASTPortInitialValueOrGuess> getInitalGuesses() {
public List<ASTPortInitial> getPortInitials() {
if (referencedComponent.isPresent())
return referencedComponent.get().getInitalGuesses();
return initalGuesses;
return referencedComponent.get().getPortInitials();
return portInitials;
}
public void addInitialGuess(ASTPortInitialValueOrGuess initialGuess) {
public void addPortInitial(ASTPortInitial initialGuess) {
if (referencedComponent.isPresent())
referencedComponent.get().addInitialGuess(initialGuess);
referencedComponent.get().addPortInitial(initialGuess);
else
this.initalGuesses.add(initialGuess);
this.portInitials.add(initialGuess);
}
public void setPortInitials(List<ASTPortInitial> initalGuesses) {
this.portInitials = initalGuesses;
}
public List<ASTComponentModifier> getComponentModifiers() {
if (referencedComponent.isPresent())
return referencedComponent.get().getComponentModifiers();
return componentModifiers;
}
public void addComponentModifier(ASTComponentModifier componentModifier) {
if (referencedComponent.isPresent())
referencedComponent.get().addComponentModifier(componentModifier);
else
this.componentModifiers.add(componentModifier);
}
public void setComponentModifiers(List<ASTComponentModifier> componentModifiers) {
this.componentModifiers = componentModifiers;
}
public boolean isNonVirtual() {
for (ASTComponentModifier componentModifier : componentModifiers) {
if (componentModifier instanceof ASTVirtModifier) {
if (((ASTVirtModifier) componentModifier).getVIRTUAL().equals(ASTVIRTUAL.NONVIRTUAL))
return true;
else
return false;
}
}
return false;
}
public boolean isVirtual() {
return !isNonVirtual();
}
public boolean isNonDirectFeedThrough() {
for (ASTComponentModifier componentModifier : componentModifiers) {
if (componentModifier instanceof ASTDFModifier) {
if (((ASTDFModifier) componentModifier).getDIRECTFEEDTHROUGH().equals(ASTDIRECTFEEDTHROUGH.NONDF))
return true;
else
return false;
}
}
return false;
}
public void setInitialGuesses(List<ASTPortInitialValueOrGuess> initalGuesses) {
this.initalGuesses = initalGuesses;
public boolean isDirectFeedThrough() {
return !isNonDirectFeedThrough();
}
}
......@@ -6,7 +6,8 @@ package de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.cncMode
import com.google.common.collect.ImmutableList;
import de.monticore.expressionsbasis._ast.ASTExpression;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.ASTPortInitialValueOrGuess;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.ASTComponentModifier;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.ASTPortInitial;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.EmbeddedMontiArcSymbolTableCreator;
import de.monticore.lang.monticar.si._symboltable.ResolutionDeclarationSymbol;
import de.monticore.symboltable.MutableScope;
......@@ -32,7 +33,8 @@ public class EMAComponentSymbolReference extends EMAComponentSymbol implements
private List<ResolutionDeclarationSymbol> resSymbols = new ArrayList<>();
private List<ASTExpression> arguments = new ArrayList<>();
private List<ASTPortInitialValueOrGuess> initalGuesses = new ArrayList<>();
private List<ASTPortInitial> portInitials = new ArrayList<>();
private List<ASTComponentModifier> componentModifiers = new ArrayList<>();
public EMAComponentSymbolReference(final String name, final Scope definingScopeOfReference) {
super(name);
......@@ -164,18 +166,33 @@ public class EMAComponentSymbolReference extends EMAComponentSymbol implements
}
@Override
public List<ASTPortInitialValueOrGuess> getInitalGuesses() {
return this.initalGuesses;
public List<ASTPortInitial> getPortInitials() {
return this.portInitials;
}
@Override
public void addInitialGuess(ASTPortInitialValueOrGuess initialGuess) {
this.initalGuesses.add(initialGuess);
public void addPortInitial(ASTPortInitial initialGuess) {
this.portInitials.add(initialGuess);
}
@Override
public void setInitialGuesses(List<ASTPortInitialValueOrGuess> initalGuesses) {
this.initalGuesses = initalGuesses;
public void setPortInitials(List<ASTPortInitial> portInintials) {
this.portInitials = portInintials;
}
@Override
public List<ASTComponentModifier> getComponentModifiers() {
return this.componentModifiers;
}
@Override
public void addComponentModifier(ASTComponentModifier componentModifier) {
this.componentModifiers.add(componentModifier);
}
@Override
public void setComponentModifiers(List<ASTComponentModifier> componentModifiers) {
this.componentModifiers = componentModifiers;
}
/* Methods of Symbol interface */
......
......@@ -3,7 +3,8 @@ package de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.instanc
import de.monticore.expressionsbasis._ast.ASTExpression;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.ASTComponent;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.ASTPortInitialValueOrGuess;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.ASTComponentModifier;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.ASTPortInitial;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._ast.EmbeddedMontiArcMill;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.UnitNumberExpressionSymbol;
import de.monticore.lang.embeddedmontiarc.embeddedmontiarc._symboltable.cncModel.*;
......@@ -16,6 +17,7 @@ import de.monticore.lang.monticar.resolution._ast.ASTUnitNumberResolutionExpress
import de.monticore.lang.monticar.si._symboltable.ResolutionDeclarationSymbol;
import de.monticore.lang.monticar.ts.MCTypeSymbol;
import de.monticore.lang.monticar.ts.references.MCTypeReference;
import de.monticore.lang.monticar.types2._ast.ASTElementType;
import de.monticore.literals.literals._ast.*;
import de.monticore.numberunit._ast.ASTNumberWithInf;
import de.monticore.numberunit._ast.ASTNumberWithUnit;
......@@ -45,10 +47,11 @@ public class EMAComponentInstanceBuilder {
protected List<EMAVariable> parameters = new ArrayList<>();
protected List<ASTExpression> arguments = new ArrayList<>();
protected String packageName = "";
protected List<ASTPortInitialValueOrGuess> initialGuesses = new ArrayList<>();
protected List<ASTPortInitial> portInitials = new ArrayList<>();
protected List<ASTComponentModifier> componentModifiers = new ArrayList<>();
protected static Map<MCTypeSymbol, ActualTypeArgument> createMap(List<MCTypeSymbol> keys,
List<ActualTypeArgument> values) {
List<ActualTypeArgument> values) {
Map<MCTypeSymbol, ActualTypeArgument> ret = new LinkedHashMap<>();
for (int i = 0; i < keys.size(); i++) {
ret.put(keys.get(i), values.get(i));
......@@ -79,6 +82,8 @@ public class EMAComponentInstanceBuilder {
.addResolutionDeclarationSymbols(inst.getResolutionDeclarationSymbols())
.addParameters(inst.getParameters())
.addArguments(inst.getArguments())
.addPortInitials(inst.getComponentType().getPortInitials())
.addComponentModifiers(inst.getComponentModifiers())
.setPackageName(inst.getPackageName());
return res.build();
......@@ -124,13 +129,13 @@ public class EMAComponentInstanceBuilder {
}
public EMAComponentInstanceBuilder addActualTypeArgument(MCTypeSymbol formalTypeParameter,
ActualTypeArgument typeArgument) {
ActualTypeArgument typeArgument) {
this.actualTypeArguments.put(formalTypeParameter, typeArgument);
return this;
}
public EMAComponentInstanceBuilder addActualTypeArguments(List<MCTypeSymbol> formalTypeParameters,
List<ActualTypeArgument> actualTypeArguments) {
List<ActualTypeArgument> actualTypeArguments) {
if (formalTypeParameters.size() != actualTypeArguments.size()) {
Log.debug(formalTypeParameters.toString(), "FormalTypeParameters");
Log.debug(actualTypeArguments.toString(), "ActualTypeArguments");
......@@ -158,7 +163,7 @@ public class EMAComponentInstanceBuilder {
* adds ports if they do not exist and replace generics of ports
*/
public EMAComponentInstanceBuilder addPortsIfNameDoesNotExists(Collection<EMAPortSymbol> ports,
List<MCTypeSymbol> formalTypeParameters, List<ActualTypeArgument> actualTypeArguments) {
List<MCTypeSymbol> formalTypeParameters, List<ActualTypeArgument> actualTypeArguments) {
List<EMAPortSymbol> pList = ports.stream().collect(Collectors.toList());
createMap(formalTypeParameters, actualTypeArguments).forEach((k, v) ->
ports.stream().filter(p -> p.getTypeReference().getReferencedSymbol().getName().equals(k.getName()))
......@@ -226,7 +231,7 @@ public class EMAComponentInstanceBuilder {
}
protected void exchangeGenerics(EMAComponentInstanceSymbol inst,
Map<MCTypeSymbol, ActualTypeArgument> mapTypeArguments) {
Map<MCTypeSymbol, ActualTypeArgument> mapTypeArguments) {
Log.debug(inst.toString(), "exchangeGenerics inst");
// TODO work with full names, but then you got the problem with generics.GenericInstance.Generic.T != generics.SuperGenericComparableComp2.T
// because when delegating the name of the referenced type must be created
......@@ -245,14 +250,24 @@ public class EMAComponentInstanceBuilder {
// now update the actual type reference definitions by replacing them according to the hash map
.forEachOrdered(
s -> s.setActualTypeArguments(
s.getActualTypeArguments().stream()
// replace this filtered type arguments with the value we want to replace
// .map(a -> a.getType().getReferencedSymbol().getFullName().equals(k.getFullName()) ? v : a)
.map(a -> (a.getType().existsReferencedSymbol() ? (
a.getType().getReferencedSymbol().getName().equals(k.getName()) ? v : a)
: a))
.collect(Collectors.toList())
));
s.getActualTypeArguments().stream()
// replace this filtered type arguments with the value we want to replace
// .map(a -> a.getType().getReferencedSymbol().getFullName().equals(k.getFullName()) ? v : a)
.map(a -> (a.getType().existsReferencedSymbol() ? (
a.getType().getReferencedSymbol().getName().equals(k.getName()) ? v : a)
: a))
.collect(Collectors.toList())
));
// 3) replace Parameter generics
// TODO more, maybe ranges etc. Parameter types should have the same class as all other types...
inst.getParameters().stream()
.filter(emaVariable -> emaVariable.getType() instanceof ASTElementType
&& ((ASTElementType) emaVariable.getType()).isPresentName()
&& ((ASTElementType) emaVariable.getType()).getName().equals(k.getName()))
.forEach(emaVariable -> {
((ASTElementType) emaVariable.getType()).setName(v.getType().getName());
});
});
......@@ -339,9 +354,9 @@ public class EMAComponentInstanceBuilder {
return null;
}
public void addPortArraySymbolsToInstance(EMAComponentInstanceSymbol instance){
public void addPortArraySymbolsToInstance(EMAComponentInstanceSymbol instance) {
Map<String, List<EMAPortInstanceSymbol>> nameToPortList = new HashMap<>();
for (EMAPortInstanceSymbol port : instance.getPortInstanceList()){
for (EMAPortInstanceSymbol port : instance.getPortInstanceList()) {
List<EMAPortInstanceSymbol> list = nameToPortList
.computeIfAbsent(port.getNameWithoutArrayBracketPart(), k -> new ArrayList<>());
list.add(port);
......@@ -350,7 +365,7 @@ public class EMAComponentInstanceBuilder {
}
}
for (String name : nameToPortList.keySet()){
for (String name : nameToPortList.keySet()) {
if (!instance.getSpannedScope().resolveLocally(name, EMAPortArraySymbol.KIND).isPresent()) {
List<EMAPortInstanceSymbol> ports = nameToPortList.get(name);
EMAPortArraySymbol portArray = new EMAPortArraySymbol(name, null);
......@@ -379,8 +394,10 @@ public class EMAComponentInstanceBuilder {
ports.stream().forEachOrdered(p ->
handlePort(p, componentFullName, scope)); // must be cloned since we change it if it has
addPortArraySymbolsToInstance(sym);
Collection<EMAPortInstanceSymbol> portInstances = scope.resolveLocally(EMAPortInstanceSymbol.KIND);
handleInitialGuesses(sym);
handlePortInitials(sym);
// Component Modifiers
sym.setComponentModifiers(componentModifiers);
// generics
connectors.stream().forEachOrdered(c -> instantiateConnectorSymbol(c, componentFullName, scope));
......@@ -411,9 +428,9 @@ public class EMAComponentInstanceBuilder {
throw new Error("not all parameters have been set before to build the expanded component instance symbol");
}
private void handleInitialGuesses(EMAComponentInstanceSymbol sym) {
private void handlePortInitials(EMAComponentInstanceSymbol sym) {
Collection<EMAPortInstanceSymbol> portInstanceList = sym.getPortInstanceList();
for (ASTPortInitialValueOrGuess initialGuess : initialGuesses) {
for (ASTPortInitial initialGuess : portInitials) {
String arrayAccess = "";
if (initialGuess.isPresentUnitNumberResolution())
arrayAccess += "[" + initialGuess.getUnitNumberResolution().getNumber().get().intValue() + "]";
......@@ -421,7 +438,7 @@ public class EMAComponentInstanceBuilder {
portInstanceList.stream()
.filter(port -> port.getName().equals(portAccessName))
.forEachOrdered(port -> {
if(initialGuess.isGuess())
if (initialGuess.isGuess())
port.setInitialGuess(initialGuess.getExpression());
else
port.setInitialValue(initialGuess.getExpression());
......@@ -453,7 +470,7 @@ public class EMAComponentInstanceBuilder {
}
protected void instantiateConnectorSymbol(EMAConnectorSymbol c, String fullName,
MutableScope scope) {
MutableScope scope) {
scope.add(EMAConnectorBuilder.instantiate(c, fullName));
}
......@@ -473,7 +490,7 @@ public class EMAComponentInstanceBuilder {
protected void instantiatePortArraySymbol(EMAPortArraySymbol port, String packageName, MutableScope scope) {
for (int i = 0; i < port.getDimension(); ++i) {
String portName = port.getName() + "[" + (i + 1) + "]";
instantiatePortSymbol(port, packageName, portName, scope);
instantiatePortSymbol(port, packageName, portName, scope);
}
}
......@@ -618,15 +635,37 @@ public class EMAComponentInstanceBuilder {
return this;
}
public EMAComponentInstanceBuilder addInitialGuesses(List<ASTPortInitialValueOrGuess> initialGuesses) {
for (ASTPortInitialValueOrGuess initialGuess : initialGuesses) {
if (!this.initialGuesses.contains(initialGuess))
this.initialGuesses.add(initialGuess);
public EMAComponentInstanceBuilder addPortInitials(List<ASTPortInitial> portInitials) {
for (ASTPortInitial portInitial : portInitials) {
if (!this.portInitials.contains(portInitial))
this.portInitials.add(portInitial);
}
return this;
}
public List<ASTPortInitial> getPortInitials() {
return portInitials;
}
public EMAComponentInstanceBuilder setPortInitials(List<ASTPortInitial> portInitials) {
this.portInitials = portInitials;
return this;
}
<