Exemplo n.º 1
0
        public void TypedEquals_DifferentFromNull()
        {
            var element1 = new LexicalElement(LexicalElementType.Term, "test", 12);
            var element2 = (LexicalElement)null;

            Assert.False(element1.Equals(element2));
        }
Exemplo n.º 2
0
        public void TypedEquals_AllEquals()
        {
            var element1 = new LexicalElement(LexicalElementType.Term, "test", 12);
            var element2 = new LexicalElement(LexicalElementType.Term, "test", 12);

            Assert.True(element1.Equals(element2));
        }
Exemplo n.º 3
0
        public void TypedEquals_DifferentPosition()
        {
            var element1 = new LexicalElement(LexicalElementType.Term, "test", 12);
            var element2 = new LexicalElement(LexicalElementType.Term, "test", 19);

            Assert.False(element1.Equals(element2));
        }
Exemplo n.º 4
0
        public void ToStringContainsTypeAndText()
        {
            var element = new LexicalElement(LexicalElementType.Term, "test", 12);

            Assert.Contains("Term", element.ToString());
            Assert.Contains("test", element.ToString());
        }
Exemplo n.º 5
0
        public void GetHashcode()
        {
            var element1 = new LexicalElement(LexicalElementType.Term, "test", 12);
            var element2 = new LexicalElement(LexicalElementType.Term, "test", 12);

            var element3 = new LexicalElement(LexicalElementType.Or, "test", 12);
            var element4 = new LexicalElement(LexicalElementType.Term, "nono", 12);
            var element5 = new LexicalElement(LexicalElementType.Term, "test", 19);

            var element6 = new LexicalElement(LexicalElementType.Or, null, 12);
            var element7 = new LexicalElement(LexicalElementType.Term, null, 12);
            var element8 = new LexicalElement(LexicalElementType.Term, null, 19);

            Assert.Equal(element1.GetHashCode(), element2.GetHashCode());
            Assert.NotEqual(element1.GetHashCode(), element3.GetHashCode());
            Assert.NotEqual(element1.GetHashCode(), element4.GetHashCode());
            Assert.NotEqual(element1.GetHashCode(), element5.GetHashCode());
            Assert.NotEqual(element1.GetHashCode(), element6.GetHashCode());
            Assert.NotEqual(element1.GetHashCode(), element7.GetHashCode());
            Assert.NotEqual(element1.GetHashCode(), element8.GetHashCode());

            Assert.NotEqual(element3.GetHashCode(), element4.GetHashCode());
            Assert.NotEqual(element3.GetHashCode(), element5.GetHashCode());

            Assert.NotEqual(element6.GetHashCode(), element7.GetHashCode());
            Assert.NotEqual(element6.GetHashCode(), element8.GetHashCode());
        }
        public void EmptyArrayParameter()
        {
            var input  = new LexicalElement[0];
            var output = Parser.GetPostfixNotation(input);

            Assert.True(output.Length == 0);
        }
Exemplo n.º 7
0
        public void ObjectEquals_AllDifferent()
        {
            object element1 = new LexicalElement(LexicalElementType.Term, "test", 12);
            object element2 = new LexicalElement(LexicalElementType.Or, "nono", 19);

            Assert.False(element1.Equals(element2));
        }
        public void LexicalElementConstructor()
        {
            var element = new LexicalElement(LexicalElementType.And, "test", 15);
            var ex      = new ParsingException(element);

            Assert.Equal(element, ex.LexicalElement);
        }
Exemplo n.º 9
0
        private static bool ElementInSpan(SnapshotSpan span, SnapshotSpan target, LexicalElement element)
        {
            var classificationSpan = new Span(span.Start.Position + element.StartCursor.Location,
                                              element.EndCursor.Location - element.StartCursor.Location);

            return(classificationSpan.IntersectsWith(target));
        }
Exemplo n.º 10
0
        public void ObjectEquals_AllEquals()
        {
            object element1 = new LexicalElement(LexicalElementType.Term, "test", 12);
            object element2 = new LexicalElement(LexicalElementType.Term, "test", 12);

            Assert.True(element1.Equals(element2));
        }
Exemplo n.º 11
0
        public void ObjectEquals_NotLexicalElement()
        {
            object element1 = new LexicalElement(LexicalElementType.Term, "test", 12);
            object element2 = -159;

            Assert.False(element1.Equals(element2));
        }
Exemplo n.º 12
0
        public void WorkingConstructorAndGetters()
        {
            var element = new LexicalElement(LexicalElementType.Term, "test", 12);

            Assert.Equal(LexicalElementType.Term, element.Type);
            Assert.Equal("test", element.Text);
            Assert.Equal(12, element.Position);
        }
        public void MessageLexicalElementErrorKindConstructor()
        {
            var element = new LexicalElement(LexicalElementType.And, "test", 15);
            var ex      = new ParsingException("test2", element, ErrorKind.MissingLeftParenthesis);

            Assert.Equal("test2", ex.Message);
            Assert.Equal(element, ex.LexicalElement);
            Assert.Equal(ErrorKind.MissingLeftParenthesis, ex.ErrorKind);
        }
Exemplo n.º 14
0
        /// <summary>
        /// Determines whether the given bracket is the only thing on its line or whether it shares the line.
        /// </summary>
        /// <param name="bracketNode">The bracket to check.</param>
        /// <param name="allowTrailingCharacters">Indicates whether a semicolon, comma or closing parenthesis after the
        /// bracket is allowed.</param>
        /// <returns>Returns true if the bracket shares the line with something else.</returns>
        private static bool BracketSharesLine(Token bracketNode, bool allowTrailingCharacters)
        {
            Param.AssertNotNull(bracketNode, "bracketNode");
            Param.Ignore(allowTrailingCharacters);

            // Look forward.
            bool sharesLine = false;

            // Find the next non-whitespace or comment item.
            LexicalElement nextItem = null;

            for (LexicalElement item = bracketNode.FindNextLexicalElement(); item != null; item = item.FindNextLexicalElement())
            {
                if (item.LexicalElementType == LexicalElementType.EndOfLine)
                {
                    break;
                }
                else if (item.LexicalElementType != LexicalElementType.WhiteSpace &&
                         item.LexicalElementType != LexicalElementType.Comment)
                {
                    nextItem = item;
                    break;
                }
            }

            if (nextItem != null)
            {
                if (!allowTrailingCharacters ||
                    (!nextItem.Is(TokenType.Semicolon) &&
                     !nextItem.Is(TokenType.Comma) &&
                     !nextItem.Is(TokenType.CloseParenthesis) &&
                     !nextItem.Is(TokenType.CloseSquareBracket)))
                {
                    sharesLine = true;
                }
            }

            if (!sharesLine)
            {
                // Look backwards.
                for (LexicalElement item = bracketNode.FindPreviousLexicalElement(); item != null; item = item.FindPreviousLexicalElement())
                {
                    if (item.LexicalElementType == LexicalElementType.EndOfLine)
                    {
                        break;
                    }
                    else if (item.LexicalElementType != LexicalElementType.WhiteSpace && !item.Is(CommentType.SingleLineComment))
                    {
                        sharesLine = true;
                        break;
                    }
                }
            }

            return(sharesLine);
        }
        public void MessageInnerExceptionLexicalElementConstructor()
        {
            var innerException = new Exception();
            var element        = new LexicalElement(LexicalElementType.And, "test", 15);
            var ex             = new ParsingException("test2", innerException, element);

            Assert.Equal("test2", ex.Message);
            Assert.Equal(element, ex.LexicalElement);
            Assert.Equal(innerException, ex.InnerException);
        }
Exemplo n.º 16
0
        /// <summary>
        /// Checks the line spacing within the given document.
        /// </summary>
        /// <param name="document">The document to check.</param>
        private void CheckLineSpacing(CsDocument document)
        {
            Param.AssertNotNull(document, "document");

            // Set up some variables.
            int            count            = 0;
            LexicalElement precedingItem    = null;
            bool           fileHeader       = true;
            bool           firstTokenOnLine = true;

            // Loop through all the tokens in the document.
            for (LexicalElement item = document.FindFirstDescendentLexicalElement(); item != null; item = item.FindNextDescendentLexicalElementOf(document))
            {
                // Check for cancel.
                if (this.Cancel)
                {
                    break;
                }

                // Check whether we're through the file header yet.
                if (fileHeader &&
                    item.LexicalElementType != LexicalElementType.EndOfLine &&
                    item.LexicalElementType != LexicalElementType.WhiteSpace &&
                    item.LexicalElementType != LexicalElementType.Comment)
                {
                    fileHeader = false;
                }

                // Check whether this token is an end-of-line character.
                if (item.Text == "\n")
                {
                    ++count;

                    // This sets up for the next token, which will the be first token on its line.
                    firstTokenOnLine = true;

                    // Process the newline character.
                    this.CheckLineSpacingNewline(precedingItem, item, count);
                }
                else if (item.LexicalElementType != LexicalElementType.WhiteSpace)
                {
                    // Process the non-whitespace character.
                    this.CheckLineSpacingNonWhitespace(document, precedingItem, item, fileHeader, firstTokenOnLine, count);

                    count = 0;

                    precedingItem = item;

                    if (firstTokenOnLine && item.LexicalElementType != LexicalElementType.Comment)
                    {
                        firstTokenOnLine = false;
                    }
                }
            }
        }
Exemplo n.º 17
0
        /// <summary>
        /// Checks a type to determine whether it should use one of the built-in types.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <param name="parentElement">The parent element.</param>
        private void CheckBuiltInType(TypeToken type, Element parentElement)
        {
            Param.AssertNotNull(type, "type");
            Param.AssertNotNull(parentElement, "parentElement");

            if (!type.IsGeneric)
            {
                for (int i = 0; i < this.builtInTypes.Length; ++i)
                {
                    string[] builtInType = this.builtInTypes[i];

                    if (type.MatchTokens(builtInType[ShortNameIndex]) ||
                        type.MatchTokens("System", ".", builtInType[ShortNameIndex]))
                    {
                        // If the previous token is an equals sign, then this is a using alias directive. For example:
                        // using SomeAlias = System.String;
                        bool usingAliasDirective = false;
                        for (LexicalElement previous = type.FindPreviousLexicalElement(); previous != null; previous = previous.FindPreviousLexicalElement())
                        {
                            if (previous.LexicalElementType != LexicalElementType.Comment &&
                                previous.LexicalElementType != LexicalElementType.WhiteSpace &&
                                previous.LexicalElementType != LexicalElementType.EndOfLine)
                            {
                                if (previous.Text == "=")
                                {
                                    usingAliasDirective = true;
                                }

                                break;
                            }
                        }

                        if (!usingAliasDirective)
                        {
                            this.Violation(
                                Rules.UseBuiltInTypeAlias,
                                new ViolationContext(parentElement, type.LineNumber, builtInType[AliasIndex], builtInType[ShortNameIndex], builtInType[LongNameIndex]),
                                (c, o) =>
                            {
                                // Insert a new type token with the correct aliased version of the type.
                                CsDocument document = type.Document;
                                TypeToken aliasType = document.CreateTypeToken(document.CreateLiteralToken(builtInType[AliasIndex]));
                                document.Replace(type, aliasType);
                            });
                        }

                        break;
                    }
                }
            }
        }
Exemplo n.º 18
0
        private IClassificationType GetClassificationForLexicalElement(LexicalElement element)
        {
            string classificationType;

            if (LexicalNameToFormat.ContainsKey(element.Name))
            {
                classificationType = LexicalNameToFormat[element.Name];
            }
            else
            {
                classificationType = "ConfigNode";
            }
            return(classificationRegistry.GetClassificationType(classificationType));
        }
Exemplo n.º 19
0
        /// <summary>
        /// Gets the non-whitespace item that appears before the given item.
        /// </summary>
        /// <param name="item">The original item.</param>
        /// <returns>Returns the previous item.</returns>
        private static LexicalElement GetPreviousNonWhitespaceItem(CodeUnit item)
        {
            Param.AssertNotNull(item, "item");

            for (LexicalElement previous = item.FindPreviousLexicalElement(); previous != null; previous = previous.FindPreviousLexicalElement())
            {
                if (!previous.Is(LexicalElementType.EndOfLine) && !previous.Is(LexicalElementType.WhiteSpace))
                {
                    return(previous);
                }
            }

            return(null);
        }
        public void SerializationRoundtripSuccessful()
        {
            var sourceLexicalElement = new LexicalElement(LexicalElementType.Term, "term", 1);
            var sourceException      = new ParsingException("test", sourceLexicalElement);

            var binaryFormatter = new BinaryFormatter();

            using (var memoryStream = new MemoryStream())
            {
                binaryFormatter.Serialize(memoryStream, sourceException);
                memoryStream.Seek(0, SeekOrigin.Begin);
                var obj = binaryFormatter.Deserialize(memoryStream);
                var destinationException = obj as ParsingException;
                Assert.NotNull(destinationException);
                Assert.Equal("test", destinationException.Message);
                Assert.Equal(sourceLexicalElement, destinationException.LexicalElement);
            }
        }
Exemplo n.º 21
0
        public ParserConfiguration(Stack <SyntaxNode> nodeStack, Stack <AnalyzerState> stateStack, Queue <LexicalElement> input, AnalyzerOperation operation)
        {
            var nsa = new SyntaxNode[nodeStack.Count];

            nodeStack.CopyTo(nsa, 0);
            Array.Reverse(nsa);
            this.nodeStack = new List <SyntaxNode>(nsa);
            var ssa = new AnalyzerState[stateStack.Count];

            stateStack.CopyTo(ssa, 0);
            Array.Reverse(ssa);
            this.stateStack = new List <AnalyzerState>(ssa);
            var ia = new LexicalElement[input.Count];

            input.CopyTo(ia, 0);
            this.input     = new List <LexicalElement>(ia);
            this.operation = operation;
        }
        /// <summary>
        /// Determines whether the given token is preceded by a member access symbol.
        /// </summary>
        /// <param name="literalToken">The token to check.</param>
        /// <returns>Returns true if the token is preceded by a member access symbol.</returns>
        private static bool IsLiteralTokenPrecededByMemberAccessSymbol(Token literalToken)
        {
            Param.AssertNotNull(literalToken, "literalToken");

            // Get the previous non-whitespace item.
            LexicalElement previousItem = ReadabilityRules.GetPreviousNonWhitespaceItem(literalToken);

            if (previousItem != null)
            {
                if (previousItem.Is(OperatorType.MemberAccess) ||
                    previousItem.Is(OperatorType.Pointer) ||
                    previousItem.Is(OperatorType.QualifiedAlias))
                {
                    return(true);
                }
            }

            return(false);
        }
Exemplo n.º 23
0
        /// <summary>
        /// Determines whether the given token is part of a file header. It is considered
        /// part of a file header if the only tokens in front of it are single-line comments,
        /// whitespace, or newlines.
        /// </summary>
        /// <param name="comment">The comment to check.</param>
        /// <returns>Returns true if the comment is part of a file header.</returns>
        private static bool IsCommentInFileHeader(Comment comment)
        {
            Param.AssertNotNull(comment, "comment");

            LexicalElement item = comment;

            while (item != null)
            {
                if (!item.Is(CommentType.SingleLineComment) &&
                    item.LexicalElementType != LexicalElementType.WhiteSpace &&
                    item.LexicalElementType != LexicalElementType.EndOfLine)
                {
                    return(false);
                }

                item = item.FindPreviousLexicalElement();
            }

            return(true);
        }
Exemplo n.º 24
0
        /// <summary>
        /// Processes a newline character found while checking line spacing rules.
        /// </summary>
        /// <param name="precedingItem">The preceding item before the newline.</param>
        /// <param name="item">The newline item.</param>
        /// <param name="count">The current newline count.</param>
        private void CheckLineSpacingNewline(LexicalElement precedingItem, LexicalElement item, int count)
        {
            Param.Ignore(precedingItem);
            Param.AssertNotNull(item, "item");
            Param.AssertGreaterThanOrEqualToZero(count, "count");

            // If we've seen two end-of-line characters in a row, then there is a blank
            // line in the code.
            if (count == 2)
            {
                // Check whether we've seen at least one token before this blank line.
                if (precedingItem != null && !precedingItem.Generated)
                {
                    if (precedingItem.Is(TokenType.OpenCurlyBracket))
                    {
                        // The blank line comes just after an opening curly bracket.
                        this.AddViolation(
                            precedingItem.FindParentElement(),
                            precedingItem.LineNumber,
                            Rules.OpeningCurlyBracketsMustNotBeFollowedByBlankLine);
                    }
                    else if (IsXmlHeader(precedingItem))
                    {
                        // The blank line comes just after an Xml header.
                        this.AddViolation(
                            precedingItem.FindParentElement(),
                            precedingItem.LineNumber,
                            Rules.ElementDocumentationHeadersMustNotBeFollowedByBlankLine);
                    }
                }
            }
            else if (count == 3 && !item.Generated)
            {
                // There are two or more blank lines in a row.
                this.AddViolation(
                    item.FindParentElement(),
                    item.LineNumber,
                    Rules.CodeMustNotContainMultipleBlankLinesInARow);
            }
        }
Exemplo n.º 25
0
 public FactorNode(SyntaxNode[] argNodes) : base(argNodes)
 {
     if (argNodes.Length == 3)
     {
         type = 3;
         e1   = (ExpressionNode)argNodes[1];
     }
     else
     {
         LexicalElement l = ((TerminalNode)argNodes[0]).Lex;
         if (l is IdentifierElement ie)
         {
             type       = 1;
             Identifier = ie.Value;
         }
         else if (l is IntegerLiteral il)
         {
             type  = 2;
             value = il.Value;
         }
     }
 }
Exemplo n.º 26
0
        private void CheckLineSpacingNonWhitespace(
            CsDocument document, LexicalElement precedingItem, LexicalElement item, bool fileHeader, bool firstTokenOnLine, int count)
        {
            Param.AssertNotNull(document, "document");
            Param.Ignore(precedingItem);
            Param.AssertNotNull(item, "item");
            Param.Ignore(fileHeader);
            Param.Ignore(firstTokenOnLine);
            Param.AssertGreaterThanOrEqualToZero(count, "count");

            // Skip generated tokens.
            if (!item.Generated)
            {
                // If there is at least one blank line in front of the current token.
                if (count > 1)
                {
                    if (item.Is(TokenType.CloseCurlyBracket))
                    {
                        // The blank line is just before a closing curly bracket.
                        this.AddViolation(
                            item.FindParentElement(),
                            item.LineNumber,
                            Rules.ClosingCurlyBracketsMustNotBePrecededByBlankLine);
                    }
                    else if (item.Is(TokenType.OpenCurlyBracket))
                    {
                        // The blank line is just before an opening curly bracket.
                        this.AddViolation(
                            item.FindParentElement(),
                            item.LineNumber,
                            Rules.OpeningCurlyBracketsMustNotBePrecededByBlankLine);
                    }
                    else if (item.Is(TokenType.Else) ||
                             item.Is(TokenType.Catch) ||
                             item.Is(TokenType.Finally))
                    {
                        // The blank line is just before an else, catch, or finally statement.
                        this.AddViolation(
                            item.FindParentElement(),
                            item.LineNumber,
                            Rules.ChainedStatementBlocksMustNotBePrecededByBlankLine);
                    }
                    else if (item.Is(TokenType.WhileDo))
                    {
                        // The blank line is just before the while keyword from a do/while statement.
                        this.AddViolation(
                            item.FindParentElement(),
                            item.LineNumber,
                            Rules.WhileDoFooterMustNotBePrecededByBlankLine);
                    }

                    // Check if there is a blank line after a single-line comment. The exceptions
                    // are if this is the file header, or if the line after the blank line contains
                    // another comment or an Xml header. This is ok if the comment is not
                    // the first item on its line.
                    if (!fileHeader &&
                        precedingItem != null &&
                        precedingItem.Is(CommentType.SingleLineComment) &&
                        !item.Is(LexicalElementType.Comment) &&
                        !IsXmlHeader(item))
                    {
                        Comment precedingComment = (Comment)precedingItem;

                        // Now check whether the comment is the first item on its line. If the comment
                        // is not the first item on the line, then this is not a violation.
                        bool itemSeen = false;
                        for (LexicalElement lineItem = precedingComment.FindPreviousLexicalElement(); lineItem != null; lineItem = lineItem.FindPreviousLexicalElement())
                        {
                            if (lineItem.LexicalElementType == LexicalElementType.EndOfLine)
                            {
                                break;
                            }
                            else if (lineItem.LexicalElementType != LexicalElementType.WhiteSpace)
                            {
                                itemSeen = true;
                                break;
                            }
                        }

                        // Now make sure this comment does not begin with '////'. If so, this is the signal
                        // that StyleCop should ignore this particular error. This is used when the
                        // developer is commenting out a line of code. In this case it is not a true comment.
                        string trimmedComment = precedingComment.Text.Trim();
                        if (!itemSeen && !trimmedComment.StartsWith(@"////", StringComparison.Ordinal))
                        {
                            // The blank line appears after a file header, we want to allow this.
                            if (!IsCommentInFileHeader(precedingComment))
                            {
                                this.AddViolation(
                                    precedingComment.FindParentElement(),
                                    precedingComment.LineNumber,
                                    Rules.SingleLineCommentsMustNotBeFollowedByBlankLine);
                            }
                        }
                    }
                }
                else if (count == 1)
                {
                    // There is one line return in front of the current token, which means
                    // the line in front of the current token is not blank. Check if the current
                    // token is an Xml header.
                    if (IsXmlHeader(item))
                    {
                        // This is a violation unless the previous line contains another
                        // Xml header, an opening curly bracket, or a preprocessor directive.
                        if (precedingItem != null &&
                            !IsXmlHeader(precedingItem) &&
                            !precedingItem.Is(TokenType.OpenCurlyBracket) &&
                            !IsPreprocessorDirective(precedingItem))
                        {
                            this.AddViolation(
                                item.FindParentElement(),
                                item.LineNumber,
                                Rules.ElementDocumentationHeaderMustBePrecededByBlankLine);
                        }
                    }
                    else if (item.Is(CommentType.SingleLineComment))
                    {
                        // The current line contains a single line comment and the previous line
                        // is not blank. This is a violation unless the previous line contains
                        // another single line comment, an opening curly bracket, a preprocessor
                        // directive, or if the last character on the previous line is a colon,
                        // which can only mean that it is a label or a case statement, in which
                        // case this is ok.
                        if (precedingItem != null &&
                            !precedingItem.Is(CommentType.SingleLineComment) &&
                            !precedingItem.Is(TokenType.OpenCurlyBracket) &&
                            !precedingItem.Is(TokenType.LabelColon) &&
                            !IsPreprocessorDirective(precedingItem))
                        {
                            // Now make sure this comment does not begin with '////'. If so, this is the signal
                            // that StyleCop should ignore this particular error. This is used when the
                            // developer is commenting out a line of code. In this case it is not a true comment.
                            string trimmedComment = item.Text.Trim();
                            if (!trimmedComment.StartsWith(@"////", StringComparison.Ordinal))
                            {
                                this.AddViolation(
                                    item.FindParentElement(),
                                    item.LineNumber,
                                    Rules.SingleLineCommentMustBePrecededByBlankLine);
                            }
                        }
                    }

                    if (precedingItem != null && precedingItem.Is(TokenType.CloseCurlyBracket))
                    {
                        // Closing curly brackets cannot be followed directly by another bracket keyword.
                        CloseCurlyBracketToken closingCurlyBracket = (CloseCurlyBracketToken)precedingItem;
                        if (closingCurlyBracket.MatchingBracket != null &&
                            closingCurlyBracket.MatchingBracket.LineNumber != closingCurlyBracket.LineNumber &&
                            firstTokenOnLine &&
                            !item.Is(TokenType.CloseCurlyBracket) &&
                            !item.Is(TokenType.Finally) &&
                            !item.Is(TokenType.Catch) &&
                            !item.Is(TokenType.WhileDo) &&
                            !item.Is(TokenType.Else) &&
                            !IsPreprocessorDirective(item))
                        {
                            this.AddViolation(
                                closingCurlyBracket.FindParentElement(),
                                closingCurlyBracket.LineNumber,
                                Rules.ClosingCurlyBracketMustBeFollowedByBlankLine);
                        }
                    }
                }
            }
        }
Exemplo n.º 27
0
 public TerminalNode(LexicalElement lex)
 {
     Lex = lex;
 }
Exemplo n.º 28
0
 internal NodeTree(string text, LexicalElement element)
 {
     this.element = element;
     Text         = text.Substring(element.StartCursor.Location, element.EndCursor.Location - element.StartCursor.Location);
 }
 protected static bool IsError(LexicalElement element) => element.Name.Contains("_");
Exemplo n.º 30
0
        public void ToStringLengthGreaterThanZero1()
        {
            var element = new LexicalElement(LexicalElementType.Term, "test", 12);

            Assert.True(element.ToString().Length > 0);
        }