Ejemplo n.º 1
0
        // Build an assembly from a list of source strings.
        public override CompilerResults CompileAssemblyFromSource(CompilerParameters options, params string[] sources)
        {
            var root = new Root();

            foreach(string code in sources)
                parser.Parse(root, code);

            if(root.CompilerErrors.Count > 0)
            {
                var results = new CompilerResults(null);
                foreach(var e in root.CompilerErrors)
                    results.Errors.Add(e);
                return results;
            }

            validator.Validate(options, root);

            if (root.CompilerErrors.Count > 0)
            {
                var results = new CompilerResults(null);
                foreach (var e in root.CompilerErrors)
                    results.Errors.Add(e);
                return results;
            }

            var codeDomEmitter = new CodeDomEmitter();
            return codeDomEmitter.Emit(options, root);
        }
Ejemplo n.º 2
0
        // Emits the codedom statement for an if conditional block.
        public static CodeStatement Emit(IfBlock ifBlock)
        {
            // Create the codedom if statement.
            var i = new CodeConditionStatement();

            // Emit the conditional statements for the if block.
            i.Condition = CodeDomEmitter.EmitCodeExpression(ifBlock.Conditional.ChildExpressions[0]);

            // Emit the lists of statements for the true and false bodies of the if block.

            // Comments need to be added in case the bodies are empty: these two properties
            // of the CodeConditionStatement can't be null.
            i.FalseStatements.Add(new CodeCommentStatement("If condition is false, execute these statements."));
            i.TrueStatements.Add(new CodeCommentStatement("If condition is true, execute these statements."));

            // Emit the statements for the true block
            foreach (var e in ifBlock.TrueBlock.ChildExpressions)
            {
                i.TrueStatements.Add(CodeDomEmitter.EmitCodeStatement(e));
            }

            // Emit the statements for the false block.
            foreach (var e in ifBlock.FalseBlock.ChildExpressions)
            {
                i.FalseStatements.Add(CodeDomEmitter.EmitCodeStatement(e));
            }

            return(i);
        }
Ejemplo n.º 3
0
        // Generates a codedom instantiation expression: new foo() or new foo[x].
        public static CodeExpression Emit(Instantiation instantiation)
        {
            // Array instantiation needs a different treatment.
            if (instantiation.IsArray)
            {
                var c = new CodeArrayCreateExpression();

                c.CreateType     = new CodeTypeReference(instantiation.Name);
                c.SizeExpression = CodeDomEmitter.EmitCodeExpression(instantiation.Parameters.ChildExpressions[0]);
                return(c);
            }
            else // Non-array instantiation
            {
                var c = new CodeObjectCreateExpression();

                // The type that is being created
                var createType = new CodeTypeReference(instantiation.Name);

                // Apply the generic type names, if any.
                foreach (var g in instantiation.GenericTypes)
                {
                    createType.TypeArguments.Add(new CodeTypeReference(g));
                }
                c.CreateType = createType;

                // Translate the instantiation parameters.
                foreach (var a in instantiation.Parameters.ChildExpressions)
                {
                    c.Parameters.Add(CodeDomEmitter.EmitCodeExpression(a));
                }

                return(c);
            }
        }
Ejemplo n.º 4
0
        // Generate a codedom throw statement
        public static CodeStatement Emit(Throw th)
        {
            var codeThrow = new CodeThrowExceptionStatement();

            codeThrow.ToThrow = CodeDomEmitter.EmitCodeExpression(th.ChildExpressions[0]);

            return(codeThrow);
        }
Ejemplo n.º 5
0
        // Generate a codedom return statement.
        public static CodeStatement Emit(Return r)
        {
            var codeMethodReturnStatement = new CodeMethodReturnStatement();

            // Attach the expression to return, if any.
            if (r.ChildExpressions.Count > 0)
            {
                codeMethodReturnStatement.Expression = CodeDomEmitter.EmitCodeExpression(r.ChildExpressions[0]);
            }

            return(codeMethodReturnStatement);
        }
Ejemplo n.º 6
0
        // Generates a codedom indexed identifier: one that is an identifier followed by an indexer: ex foo[1].
        public static CodeExpression Emit(IndexedIdentifier indexedIdentifier)
        {
            // Create the codedom indexer expression
            var codeIndex = new CodeIndexerExpression();

            // Set the object that is being indexed.
            codeIndex.TargetObject = new CodeVariableReferenceExpression(indexedIdentifier.Name);

            // Set the expression that is generating the index.
            codeIndex.Indices.Add(CodeDomEmitter.EmitCodeExpression(indexedIdentifier.ChildExpressions[0]));

            return(codeIndex);
        }
Ejemplo n.º 7
0
        // Emit a codedome expression representing a while loop.
        public static CodeStatement Emit(WhileLoop loop)
        {
            // A while loop is a for loop with no initializer or incrementor, only a condition.
            var i = new CodeIterationStatement(new CodeSnippetStatement(""),
                                               CodeDomEmitter.EmitCodeExpression(loop.Condition.ChildExpressions[0]),
                                               new CodeSnippetStatement(""));

            // Emit the statements found in the body of the while loop.
            foreach (var e in loop.ChildExpressions)
            {
                i.Statements.Add(CodeDomEmitter.EmitCodeStatement(e));
            }

            return(i);
        }
Ejemplo n.º 8
0
        // Generate a series of codedom if statements representing a switch block.
        // (Codedom doesn't support switch)
        public static CodeStatement Emit(SwitchBlock switchBlock)
        {
            // Create the expression for whatever we're switching on.
            var codeVar = CodeDomEmitter.EmitCodeExpression(switchBlock.Variable.ChildExpressions[0]);

            // Store the first and previous if statements here, since it's needed on future loops.
            CodeConditionStatement codeIf = null;
            CodeConditionStatement lastIf = null;

            foreach (var e in switchBlock.ChildExpressions)
            {
                // Get the value being compared to: the right operand.
                var right = CodeDomEmitter.EmitCodeExpression((e as CaseBlock).Variable.ChildExpressions[0]);

                // Create the next if statement.
                var nestedIf = new CodeConditionStatement();

                // Each case block will compare codeVar (left) to the right operand.
                nestedIf.Condition = new CodeBinaryOperatorExpression(codeVar, CodeBinaryOperatorType.ValueEquality, right);
                nestedIf.TrueStatements.Add(new CodeCommentStatement("If condition is true, execute these statements."));
                foreach (var s in e.ChildExpressions)
                {
                    nestedIf.TrueStatements.Add(CodeDomEmitter.EmitCodeStatement(s));
                }
                nestedIf.FalseStatements.Add(new CodeCommentStatement("If condition is false, execute these statements."));
                // if codeIf is null, this is the first if statement.
                if (codeIf == null)
                {
                    codeIf = nestedIf;
                }
                else // It's not the first if statement, so attach it to the previous one.
                {
                    lastIf.FalseStatements.Add(nestedIf);
                }


                // Store the last if block for the next iteration.
                lastIf = nestedIf;
            }

            return(codeIf);
        }
Ejemplo n.º 9
0
        // Generate the codedom expression for a method invocation.
        public static CodeExpression Emit(MethodInvocation methodInvocation)
        {
            CodeTypeReferenceExpression t = null;

            // Check if it's a method owned by a variable.
            if (methodInvocation.Prefix != "")
            {
                t = new CodeTypeReferenceExpression(methodInvocation.Prefix);
            }

            // Create the codedom method invcation.
            var mi = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(t, methodInvocation.Name));

            // Add the parameters of the method invocation.
            foreach (var a in methodInvocation.Parameters.ChildExpressions)
            {
                mi.Parameters.Add(CodeDomEmitter.EmitCodeExpression(a));
            }

            return(mi);
        }
Ejemplo n.º 10
0
        // Generate a codedom exception handler statement
        public static CodeStatement Emit(ExceptionHandler ex)
        {
            // Create the codedom exception handler statement
            var codeTry = new CodeTryCatchFinallyStatement();

            // Add the statements in the try block
            foreach (var e in ex.Try.ChildExpressions)
            {
                codeTry.TryStatements.Add(CodeDomEmitter.EmitCodeStatement(e));
            }

            // Add all the catch clauses.
            foreach (var c in ex.CatchClauses)
            {
                // Create the codedom catch statement.
                var catchClause = new CodeCatchClause();
                // To do: replace with non-test code
                catchClause.CatchExceptionType = new CodeTypeReference("System.Exception");
                catchClause.LocalName          = "ex";
                codeTry.CatchClauses.Add(catchClause);

                // Add the statemnts in the catch block
                foreach (var e in c.ChildExpressions)
                {
                    catchClause.Statements.Add(CodeDomEmitter.EmitCodeStatement(e));
                }
            }

            // Add the statements in the finally block.
            foreach (var e in ex.Finally.ChildExpressions)
            {
                codeTry.FinallyStatements.Add(CodeDomEmitter.EmitCodeStatement(e));
            }

            return(codeTry);
        }
Ejemplo n.º 11
0
        // Generate a codedom method declaration and attach it to the given codedom type.
        public static void Emit(CodeTypeDeclaration codeTypeDeclaration, MethodDeclaration methodDeclaration)
        {
            // Create the codedom member method and attach it to the codedom type.
            var codeMemberMethod = new CodeMemberMethod();

            codeTypeDeclaration.Members.Add(codeMemberMethod);

            // Assign the method name
            codeMemberMethod.Name = methodDeclaration.Name;

            // Assign the return type: make sure to check for null.
            if (methodDeclaration.ReturnTypeName == "void")
            {
                codeMemberMethod.ReturnType = null;
            }
            else
            {
                codeMemberMethod.ReturnType = new CodeTypeReference(methodDeclaration.ReturnTypeName);
            }

            // Translate the method parameters.
            foreach (Expression p in methodDeclaration.Parameters)
            {
                if (p is SimpleParameter) // For example "int i"
                {
                    codeMemberMethod.Parameters.Add(new CodeParameterDeclarationExpression((p as SimpleParameter).TypeName, (p as SimpleParameter).Name));
                }
                if (p is DirectionedParameter) // For example "ref int i"
                {
                    var codeParameter = new CodeParameterDeclarationExpression((p as DirectionedParameter).TypeName, (p as DirectionedParameter).Name);
                    switch ((p as DirectionedParameter).Direction)
                    {
                    case ParameterDirection.Out:
                        codeParameter.Direction = FieldDirection.Out;
                        break;

                    case ParameterDirection.Ref:
                        codeParameter.Direction = FieldDirection.Ref;
                        break;
                    }
                    codeMemberMethod.Parameters.Add(codeParameter);
                }
            }

            // Translate the method's accessibility
            MemberAttributes memberAttributes = MemberAttributes.Public;

            switch (methodDeclaration.Accessibility)
            {
            case Accessibility.Internal:
                memberAttributes = MemberAttributes.FamilyAndAssembly;
                break;

            case Accessibility.Private:
                memberAttributes = MemberAttributes.Private;
                break;

            case Accessibility.Protected:
                memberAttributes = MemberAttributes.Family;
                break;

            case Accessibility.Public:
                memberAttributes = MemberAttributes.Public;
                break;
            }

            // Shared = static
            if (methodDeclaration.IsShared)
            {
                memberAttributes |= MemberAttributes.Static;
            }
            if (methodDeclaration.IsAbstract)
            {
                memberAttributes |= MemberAttributes.Abstract;
            }
            if (!methodDeclaration.IsVirtual && !methodDeclaration.IsAbstract)
            {
                if (methodDeclaration.IsOverride)
                {
                    memberAttributes |= MemberAttributes.Override;
                }
                else
                {
                    memberAttributes |= MemberAttributes.Final;
                }
            }

            codeMemberMethod.Attributes = memberAttributes;

            // Add the statements found in the method body.
            foreach (var e in methodDeclaration.ChildExpressions)
            {
                codeMemberMethod.Statements.Add(CodeDomEmitter.EmitCodeStatement(e));
            }
        }
Ejemplo n.º 12
0
        // Emit a codedom binary operation expression
        public static CodeExpression Emit(BinaryOperator op)
        {
            // Translate the operator type to a codedom one.
            CodeBinaryOperatorType opType = CodeBinaryOperatorType.Add;

            switch (op.OperatorType)
            {
            case BinaryOperatorType.Subtract:
                opType = CodeBinaryOperatorType.Subtract;
                break;

            case BinaryOperatorType.Divide:
                opType = CodeBinaryOperatorType.Divide;
                break;

            case BinaryOperatorType.Multiply:
                opType = CodeBinaryOperatorType.Multiply;
                break;

            case BinaryOperatorType.Less:
                opType = CodeBinaryOperatorType.LessThan;
                break;

            case BinaryOperatorType.LessOrEqual:
                opType = CodeBinaryOperatorType.LessThanOrEqual;
                break;

            case BinaryOperatorType.Equal:
                opType = CodeBinaryOperatorType.IdentityEquality;
                break;

            case BinaryOperatorType.NotEqual:
                opType = CodeBinaryOperatorType.IdentityInequality;
                break;

            case BinaryOperatorType.Greater:
                opType = CodeBinaryOperatorType.GreaterThan;
                break;

            case BinaryOperatorType.GreaterOrEqual:
                opType = CodeBinaryOperatorType.GreaterThanOrEqual;
                break;

            case BinaryOperatorType.BitwiseAnd:
                opType = CodeBinaryOperatorType.BitwiseAnd;
                break;

            case BinaryOperatorType.BitwiseOr:
                opType = CodeBinaryOperatorType.BitwiseOr;
                break;

            case BinaryOperatorType.Modulo:
                opType = CodeBinaryOperatorType.Modulus;
                break;

            case BinaryOperatorType.LogicalAnd:
                opType = CodeBinaryOperatorType.BooleanAnd;
                break;

            case BinaryOperatorType.LogicalOr:
                opType = CodeBinaryOperatorType.BooleanOr;
                break;

            case BinaryOperatorType.As:     // Casting requires a different set of codedom expressions.
                var cast = new CodeCastExpression();
                cast.TargetType = new CodeTypeReference((op.ChildExpressions[1] as VariableReference).Name);
                cast.Expression = CodeDomEmitter.EmitCodeExpression(op.ChildExpressions[0]);
                return(cast);
            }

            var o = new CodeBinaryOperatorExpression(CodeDomEmitter.EmitCodeExpression(op.ChildExpressions[0]),  // left operand
                                                     opType,                                                     // operator
                                                     CodeDomEmitter.EmitCodeExpression(op.ChildExpressions[1])); // right operand

            return(o);
        }
Ejemplo n.º 13
0
        // Emit a codedome expression representing a for loop.
        public static CodeStatement Emit(ForLoop loop)
        {
            // The expression describing how the for loop starts.
            CodeExpression startEx = null;
            // The expression describing how the for loop ends.
            CodeExpression endEx = null;

            // The for loop has both a start and end defined. For example: for int i in 0 to 10
            if (loop.Condition.ChildExpressions.Count > 1)
            {
                startEx = CodeDomEmitter.EmitCodeExpression(loop.Condition.ChildExpressions[0]);
                endEx   = CodeDomEmitter.EmitCodeExpression(loop.Condition.ChildExpressions[1]);
            }
            else // The for loop has only the end defined. For examle: for int i in 10
            {
                startEx = new CodePrimitiveExpression(0);
                endEx   = CodeDomEmitter.EmitCodeExpression(loop.Condition.ChildExpressions[0]);
            }

            // The statement that initializes the for loop (int i, i, etc).
            CodeStatement varInit = null;
            // The variable reference from the initialization: we need this when the initialization is "int i".
            // The variable reference is "i".
            CodeExpression varRef = null;

            // An intializer that is an explicit variable declaration must be handled differently.
            if (loop.Initialization.ChildExpressions[0] is ExplicitVariableDeclaration)
            {
                // Get the explicit variable declaration
                var explicitVariable = loop.Initialization.ChildExpressions[0] as ExplicitVariableDeclaration;
                varInit = new CodeVariableDeclarationStatement(explicitVariable.TypeName, explicitVariable.Name, // The declaration
                                                               startEx);                                         // The assignment
                varRef = new CodeVariableReferenceExpression((loop.Initialization.ChildExpressions[0] as ExplicitVariableDeclaration).Name);
            }
            else // It's a variable reference
            {
                // Get the variable reference.
                var variable = loop.Initialization.ChildExpressions[0] as VariableReference;
                varRef  = new CodeVariableReferenceExpression((loop.Initialization.ChildExpressions[0] as VariableReference).Name);
                varInit = new CodeAssignStatement(varRef, // Left operand
                                                  startEx);
            }

            // Pie's for loop is limited to less than or equal conditional.
            var op = new CodeBinaryOperatorExpression(varRef,
                                                      CodeBinaryOperatorType.LessThanOrEqual,
                                                      endEx);

            // The step: the amount incremented or decremented.
            CodeStatement stepSt = null;

            if (loop.Step.ChildExpressions[0] is Literal)
            {
                var literal = loop.Step.ChildExpressions[0] as Literal;
                stepSt = new CodeAssignStatement(varRef, new CodeBinaryOperatorExpression(
                                                     varRef, CodeBinaryOperatorType.Add, new CodePrimitiveExpression(literal.Value)));
            }
            var i = new CodeIterationStatement(varInit,
                                               op,
                                               stepSt);

            // Emit the statements found in the body of the while loop.
            foreach (var e in loop.ChildExpressions)
            {
                i.Statements.Add(CodeDomEmitter.EmitCodeStatement(e));
            }

            return(i);
        }
Ejemplo n.º 14
0
        // Generate a codedom property expression and attach it to the codedom type.
        public static void Emit(CodeTypeDeclaration codeTypeDeclaration, Property property)
        {
            // Create the codedom property and attach it to the codedom type.
            var codeProperty = new CodeMemberProperty();

            codeTypeDeclaration.Members.Add(codeProperty);

            // Assign the name.
            codeProperty.Name = property.Name;

            // Assign the return type, making sure to check for null.
            if (property.TypeName == "void")
            {
                codeProperty.Type = null;
            }
            else
            {
                codeProperty.Type = new CodeTypeReference(property.TypeName);
            }

            // Translate the accessibility.
            MemberAttributes memberAttributes = MemberAttributes.Public;

            switch (property.Accessibility)
            {
            case Accessibility.Internal:
                memberAttributes = MemberAttributes.FamilyAndAssembly;
                break;

            case Accessibility.Private:
                memberAttributes = MemberAttributes.Private;
                break;

            case Accessibility.Protected:
                memberAttributes = MemberAttributes.Family;
                break;

            case Accessibility.Public:
                memberAttributes = MemberAttributes.Public;
                break;
            }

            // Shared = static
            if (property.IsShared)
            {
                memberAttributes |= MemberAttributes.Static;
            }
            if (property.IsAbstract)
            {
                memberAttributes |= MemberAttributes.Abstract;
            }
            if (property.IsOverride)
            {
                memberAttributes |= MemberAttributes.Override;
            }

            codeProperty.Attributes = memberAttributes;

            // Add the statements for the get block.
            if (property.GetBlock.ChildExpressions.Count > 0)
            {
                foreach (var e in property.GetBlock.ChildExpressions)
                {
                    codeProperty.GetStatements.Add(CodeDomEmitter.EmitCodeStatement(e));
                }
            }
            else
            {
                codeProperty.GetStatements.Add(new CodeCommentStatement("Placeholder statement"));
            }

            // Add the statements for the set block.
            if (property.SetBlock.ChildExpressions.Count > 0)
            {
                foreach (var e in property.SetBlock.ChildExpressions)
                {
                    codeProperty.SetStatements.Add(CodeDomEmitter.EmitCodeStatement(e));
                }
            }
            else
            {
                codeProperty.SetStatements.Add(new CodeCommentStatement("Placeholder statement"));
            }
        }
Ejemplo n.º 15
0
        /* Returns a CodeDOM statement representing an Assignment expression. */
        public static CodeStatement Emit(Assignment assignment)
        {
            // The expression that is being assigned.
            var assignedExpression = CodeDomEmitter.EmitCodeExpression(assignment.ChildExpressions[1]);

            // Translate the assignment type to a CodeDOM one.
            var codeOperator = CodeBinaryOperatorType.Add;

            switch (assignment.AssignmentType)
            {
            case AssignmentType.Add:
                codeOperator = CodeBinaryOperatorType.Add;
                break;

            case AssignmentType.And:
                codeOperator = CodeBinaryOperatorType.BitwiseAnd;
                break;

            case AssignmentType.Or:
                codeOperator = CodeBinaryOperatorType.BitwiseOr;
                break;

            case AssignmentType.Divide:
                codeOperator = CodeBinaryOperatorType.Divide;
                break;

            case AssignmentType.Multiply:
                codeOperator = CodeBinaryOperatorType.Multiply;
                break;

            case AssignmentType.Subtract:
                codeOperator = CodeBinaryOperatorType.Subtract;
                break;
            }

            /* Variables that are immediately assigned a value upon explicit declaration such as:
             *      int i = 10
             * require a different CodeDOM statement than the normal assignment statement. */
            if (assignment.ChildExpressions[0] is ExplicitVariableDeclaration)
            {
                var variable = assignment.ChildExpressions[0] as ExplicitVariableDeclaration;
                var codeType = new CodeTypeReference();
                codeType.BaseType = variable.TypeName;
                foreach (String e in variable.GenericTypes)
                {
                    codeType.TypeArguments.Add(new CodeTypeReference(e));
                }
                if (assignment.AssignmentType == AssignmentType.Equal)
                {
                    return(new CodeVariableDeclarationStatement(codeType, variable.Name, // The declaration
                                                                assignedExpression));    // The assignment
                }
                else
                {
                    // If we got this far, the validator missed that int i += x is not valid.
                    // Since this should have been caught prior to reaching the generator, this is a failure state.
                    throw new NotImplementedException();
                }
            }
            else // Normal assignment (ex i = 1)
            {
                var left = CodeDomEmitter.EmitCodeExpression(assignment.ChildExpressions[0]);
                if (assignment.AssignmentType == AssignmentType.Equal)
                {
                    // A simple assignment: left = assignedExpression
                    return(new CodeAssignStatement(left,                 // Left operand
                                                   assignedExpression)); // Right operand
                }
                else
                {
                    // Some other type of assignment, such as left += assignedExpression
                    // We need to create the appropriate codedom binary expression first.
                    var addOp = new CodeBinaryOperatorExpression();
                    addOp.Left     = left;
                    addOp.Right    = assignedExpression;
                    addOp.Operator = codeOperator;
                    return(new CodeAssignStatement(left, addOp));
                }
            }
        }
Ejemplo n.º 16
0
        // Generates a codedom constructor expression and attaches it to the given type.
        public static void Emit(CodeTypeDeclaration codeTypeDeclaration, Constructor ctor)
        {
            // Create the codedom constructor
            var codeCtor = new CodeConstructor();

            codeTypeDeclaration.Members.Add(codeCtor);

            // Translate accessibility of the constructor
            MemberAttributes memberAttributes = MemberAttributes.Public;

            switch (ctor.Accessibility)
            {
            case Accessibility.Internal:
                memberAttributes |= MemberAttributes.FamilyAndAssembly;
                break;

            case Accessibility.Private:
                memberAttributes |= MemberAttributes.Private;
                break;

            case Accessibility.Protected:
                memberAttributes |= MemberAttributes.Family;
                break;

            case Accessibility.Public:
                memberAttributes |= MemberAttributes.Public;
                break;
            }
            codeCtor.Attributes = memberAttributes;

            // Translate the parameters of the constructor
            foreach (Expression p in ctor.Parameters)
            {
                if (p is SimpleParameter) // ex "int i"
                {
                    codeCtor.Parameters.Add(new CodeParameterDeclarationExpression((p as SimpleParameter).TypeName, (p as SimpleParameter).Name));
                }
                if (p is DirectionedParameter) // ex "ref int i"
                {
                    var codeParameter = new CodeParameterDeclarationExpression((p as DirectionedParameter).TypeName, (p as DirectionedParameter).Name);
                    switch ((p as DirectionedParameter).Direction)
                    {
                    case ParameterDirection.Out:
                        codeParameter.Direction = FieldDirection.Out;
                        break;

                    case ParameterDirection.Ref:
                        codeParameter.Direction = FieldDirection.Ref;
                        break;
                    }
                    codeCtor.Parameters.Add(codeParameter);
                }
            }

            // Add call to a constructor of the base class or another in the same class.
            foreach (var a in ctor.SubParameters.ChildExpressions)
            {
                if (ctor.Sub)
                {
                    codeCtor.ChainedConstructorArgs.Add(CodeDomEmitter.EmitCodeExpression(a));
                }
                else
                {
                    codeCtor.BaseConstructorArgs.Add(CodeDomEmitter.EmitCodeExpression(a));
                }
            }

            // Add all the statements in the body of the constructor
            foreach (var e in ctor.ChildExpressions)
            {
                codeCtor.Statements.Add(CodeDomEmitter.EmitCodeStatement(e));
            }
        }