示例#1
0
        public void Collection()
        {
            VariableCollection variables = new VariableCollection
            {
                new Variable("var"),
                new Variable("var"),
                new Variable("var2")
            };

            Assert.IsFalse(variables.IsReadOnly);
            Assert.AreEqual(2, variables.Count);
            {
                Variable[] arr = new Variable[variables.Count];
                variables.CopyTo(arr, 0);
                foreach (object?v in ((IEnumerable)arr))
                {
                    Assert.IsTrue(arr.Contains(v));
                }
            }
            Assert.IsTrue(variables.Contains(new Variable("var2")));
            Assert.IsTrue(variables.Remove(new Variable("var2")));
            Assert.AreEqual(1, variables.Count);
            variables.Clear();
            StringTemplate st = new StringTemplate("abc", new[] { new Variable("A") });

            variables.Collect(new[] { st });
            Assert.IsTrue(variables.Contains(new Variable("A")));
        }
示例#2
0
        public void ShouldBeAbleToClearVariablesOfCurrentScope()
        {
            _variables = new VariableCollection();

            _variables.Scopes.SetCurrent("Test Scenario 1");

            _variables.Add(_globalVariableName, _globalVariableValue);

            _variables.Count().Should().Be(1);
            _variables.Contains(_globalVariableName).Should().BeTrue();
            _variables.GetValue(_globalVariableName).Should().Be(_globalVariableValue);

            _variables.Scopes.SetCurrent("Function 1");

            _variables.Add(_localVariableName, _localVariableValue);

            _variables.Count().Should().Be(2);
            _variables.CountCurrentScopeKeys().Should().Be(1);
            _variables.Contains(_globalVariableName).Should().BeTrue();
            _variables.GetValue(_globalVariableName).Should().Be(_globalVariableValue);
            _variables.Contains(_localVariableName).Should().BeTrue();
            _variables.GetValue(_localVariableName).Should().Be(_localVariableValue);

            _variables.RemoveCurrentScopedValues();
            _variables.Count().Should().Be(1);
            _variables.CountCurrentScopeKeys().Should().Be(0);
            _variables.Contains(_globalVariableName).Should().BeTrue();
            _variables.GetValue(_globalVariableName).Should().Be(_globalVariableValue);
            _variables.Contains(_localVariableName).Should().BeFalse();
            _variables.GetValue(_localVariableName).Should().Be(null);
        }
示例#3
0
        public void ShouldBeAbleToAddVariables()
        {
            _variables = new VariableCollection();

            _variables.Scopes.SetCurrent("Test Scenario 1");

            _variables.Add(_globalVariableName, _globalVariableValue);
            _variables.Add(_localVariableName, _localVariableValue);

            _variables.Count().Should().Be(2);

            _variables.Contains(_globalVariableName).Should().BeTrue();
            _variables.GetValue(_globalVariableName).Should().Be(_globalVariableValue);

            _variables.Contains(_localVariableName).Should().BeTrue();
            _variables.GetValue(_localVariableName).Should().Be(_localVariableValue);
        }
示例#4
0
 /// <summary>Determines whether the <see cref="T:System.Collections.Generic.ICollection`1" /> contains a specific value.</summary>
 /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1" />.</param>
 /// <returns>
 /// <see langword="true" /> if <paramref name="item" /> is found in the <see cref="T:System.Collections.Generic.ICollection`1" />; otherwise, <see langword="false" />.</returns>
 public bool Contains(KeyValuePair <string, object> item)
 {
     return(_collection.Contains(item));
 }
        /// <summary>
        /// Reads the next variable declaration statement from the file and returns it.
        /// </summary>
        /// <param name="parentReference">The parent code unit.</param>
        /// <param name="unsafeCode">Indicates whether the code being parsed resides in an unsafe code block.</param>
        /// <param name="variables">Returns the list of variables defined in the statement.</param>
        /// <returns>Returns the statement.</returns>
        private VariableDeclarationStatement ParseVariableDeclarationStatement(
            Reference<ICodePart> parentReference, bool unsafeCode, VariableCollection variables)
        {
            Param.AssertNotNull(parentReference, "parentReference");
            Param.Ignore(unsafeCode);
            Param.Ignore(variables);

            bool constant = false;

            // Get the first symbol and make sure it is an unknown word or a const.
            Symbol symbol = this.GetNextSymbol(parentReference);

            CsToken firstToken = null;
            Node<CsToken> firstTokenNode = null;

            var statementReference = new Reference<ICodePart>();

            if (symbol.SymbolType == SymbolType.Const)
            {
                constant = true;

                firstToken = new CsToken(symbol.Text, CsTokenType.Const, symbol.Location, statementReference, this.symbols.Generated);
                firstTokenNode = this.tokens.InsertLast(this.GetToken(CsTokenType.Const, SymbolType.Const, statementReference));

                symbol = this.GetNextSymbol(statementReference);
            }

            if (symbol.SymbolType != SymbolType.Other)
            {
                throw this.CreateSyntaxException();
            }

            // Get the expression representing the type.
            LiteralExpression type = this.GetTypeTokenExpression(statementReference, unsafeCode, true);
            if (type == null || type.Tokens.First == null)
            {
                throw new SyntaxException(this.document.SourceCode, firstToken.LineNumber);
            }

            if (firstTokenNode == null)
            {
                firstTokenNode = type.Tokens.First;
            }

            // Get the rest of the declaration.
            VariableDeclarationExpression expression = this.GetVariableDeclarationExpression(type, ExpressionPrecedence.None, unsafeCode);

            // Get the closing semicolon.
            this.tokens.Add(this.GetToken(CsTokenType.Semicolon, SymbolType.Semicolon, statementReference));

            // Add each of the variables defined in this statement to the variable list being returned.
            if (variables != null)
            {
                VariableModifiers modifiers = constant ? VariableModifiers.Const : VariableModifiers.None;
                foreach (VariableDeclaratorExpression declarator in expression.Declarators)
                {
                    Variable variable = new Variable(
                        expression.Type,
                        declarator.Identifier.Token.Text,
                        modifiers,
                        CodeLocation.Join(expression.Type.Location, declarator.Identifier.Token.Location),
                        statementReference,
                        expression.Tokens.First.Value.Generated || declarator.Identifier.Token.Generated);

                    // There might already be a variable in this scope with the same name. This can happen
                    // in valid situation when there are ifdef's surrounding portions of the code.
                    // Just accept the first variable and ignore others.
                    if (!variables.Contains(declarator.Identifier.Token.Text))
                    {
                        variables.Add(variable);
                    }
                }
            }

            // Create the token list for the statement.
            CsTokenList partialTokens = new CsTokenList(this.tokens, firstTokenNode, this.tokens.Last);

            var statement = new VariableDeclarationStatement(partialTokens, constant, expression);
            statementReference.Target = statement;

            return statement;
        }
示例#6
0
    public static ExpressionResult Evaluate(double initialValue, string expr, Character charRef, VariableCollection variables)
    {
        expr = expr.ToUpperInvariant();
        MatchCollection  matches = rx.Matches(expr);
        Stack <double[]> values  = new Stack <double[]>();
        // Stack of tuples of the form ([operator],[order of operation])
        // where higher orders of operation are evaluated first.
        Stack <(string, int)> operators = new Stack <(string, int)>();

        // String used to keep track of what is being evaluated vs. ignored.
        // Return this with the result for useful feedback
        string evaluated_string = "";

        variables["ANS"] = new double[] { initialValue };

        foreach (Match match in matches)
        {
            if (match.Value == "")
            {
                continue;
            }
            GroupCollection groups  = match.Groups;
            double[]        value   = new double[] { 0 };
            int             opOrder = -1;

            // Insert the operator into the evaluated string
            if (groups["op"].Value != "")
            {
                if (values.Count <= 0)
                {
                    evaluated_string += $"ANS";
                    values.Push(variables["ANS"]);
                }
                evaluated_string += $" {groups["op"].Value} ";
            }

            // Insert preoperator into the evaluated string
            if (groups["pre_op"].Value != "")
            {
                evaluated_string += groups["pre_op"].Value;
            }

            // Prepare the parsed value
            if (groups["dice"].Value != "")
            {
                evaluated_string += groups["dice"].Value;

                int           d_count = Int32.Parse(groups["d_count"].Value);
                int           d_sides = Int32.Parse(groups["d_sides"].Value);
                AdvantageType adv     = AdvantageType.None;
                if (groups["d_adv"].Value != "")
                {
                    if (groups["d_adv"].Value == "a")
                    {
                        adv = AdvantageType.Advantage;
                    }
                    else if (groups["d_adv"].Value == "d")
                    {
                        adv = AdvantageType.Disadvantage;
                    }
                }
                (int[] results, int sum) = Dice.Roll(d_count, d_sides, adv);

                if (groups["reroll_nums"].Value != "")
                {
                    evaluated_string += " -";
                    int[] nums = Array.ConvertAll(groups["reroll_nums"].Value.Split(','), Int32.Parse);

                    for (int i = 0; i < results.Length; i++)
                    {
                        if (Array.IndexOf(nums, results[i]) != -1)
                        {
              #if (DEBUG)
                            Console.WriteLine("rerolling: {0}", results[i]);
              #endif
                            evaluated_string += results[i] + "-";
                            sum       -= results[i];
                            results[i] = Dice.Roll(1, d_sides).Item2;
                            sum       += results[i];
                        }
                    }
                }

                value             = Array.ConvertAll <int, double>(results, x => x);
                evaluated_string += String.Format(" ([{0}])", String.Join(",", results));
            }

            else if (groups["const"].Value != "")
            {
                if (groups["const_list"].Value != "")
                {
                    string[]      args      = SplitBalanced(',', groups["const_list"].Value);
                    List <double> tempValue = new List <double>(args.Length);
                    evaluated_string += "[";
                    for (int i = 0; i < args.Length; i++)
                    {
                        args[i] = args[i].Trim();
                        ExpressionResult result = Evaluate(args[i], charRef, variables);
                        if (!result.Success)
                        {
                            return(result);
                        }

                        if (i != 0)
                        {
                            evaluated_string += ",";
                        }
                        evaluated_string += result.Message;

                        tempValue.AddRange(result.Values);
                    }
                    value             = tempValue.ToArray();
                    evaluated_string += "]";
                }
                else
                {
                    value             = new double[] { Int32.Parse(groups["const"].Value) };
                    evaluated_string += groups["const"].Value;
                }
            }

            else if (groups["var"].Value != "")
            {
                string id = groups["var"].Value;
                if (id == "TRUE")
                {
                    value[0] = 1;
                }
                else if (id == "FALSE")
                {
                    value[0] = 0;
                }
                else if (variables.Contains(id))
                {
                    value = variables[id];
                }
                else if (charRef == null)
                {
                    return(new ExpressionResult(false, "No character given to reference", new double[] { 0 }));
                }
                else
                {
                    switch (id)
                    {
                    case "STR":
                        value[0] = charRef.GetAbilityMod("Strength");
                        break;

                    case "CON":
                        value[0] = charRef.GetAbilityMod("Constitution");
                        break;

                    case "DEX":
                        value[0] = charRef.GetAbilityMod("Dexterity");
                        break;

                    case "WIS":
                        value[0] = charRef.GetAbilityMod("Wisdom");
                        break;

                    case "INT":
                        value[0] = charRef.GetAbilityMod("Intelligence");
                        break;

                    case "CHA":
                        value[0] = charRef.GetAbilityMod("Charisma");
                        break;

                    case "PRO":
                        value[0] = charRef.GetProficiencyBonus();
                        break;

                    case "LVL":
                        value[0] = charRef.Level;
                        break;

                    case "AC":
                        value[0] = charRef.GetAC();
                        break;

                    default:
                        return(new ExpressionResult(false, $"Invalid variable name \"{id}\"", new double[] { 1 }));
                    }
                }
                evaluated_string += String.Format("{0} ({1})", id, String.Join(",", value));
            }

            else if (groups["paren"].Value != "")
            {
        #if (DEBUG)
                Console.WriteLine("{0}({1}){2}", groups["paren_mul_l"], groups["paren"], groups["paren_mul_r"]);
                Console.WriteLine("=================");
        #endif

                ExpressionResult result = Evaluate(groups["paren"].Value, charRef, variables);
                if (!result.Success)
                {
                    return(result);
                }
                value = result.Values;
                if (groups["paren_mul_l"].Value != "")
                {
                    if (groups["paren_mul_l"].Value == "-")
                    {
                        value = calculate("*", value, new double[] { (double)Int32.Parse(groups["paren_mul_l"].Value + "1") });
                    }
                    else
                    {
                        value = calculate("*", value, new double[] { (double)Int32.Parse(groups["paren_mul_l"].Value) });
                    }
                    evaluated_string += groups["paren_mul_l"].Value;
                }

                evaluated_string += $"({result.Message})";

                if (groups["paren_mul_r"].Value != "")
                {
                    value             = calculate("*", new double[] { (double)Int32.Parse(groups["paren_mul_r"].Value) }, value);
                    evaluated_string += groups["paren_mul_r"].Value;
                }


        #if (DEBUG)
                Console.WriteLine("=================");
        #endif
            }

            else if (groups["named_value"].Value != "")
            {
                string           key    = groups["name"].Value;
                ExpressionResult result = Evaluate(groups["named_inside"].Value, charRef, variables);
                if (!result.Success)
                {
                    return(result);
                }
                value             = result.Values;
                evaluated_string += $"{key}{{{result.Message}}}";
                variables[key]    = value;
            }

            else if (groups["list_op"].Value != "")
            {
                string opName = groups["list_op"].Value;

                int resultCount = 1;
                if (groups["list_count"].Value != "")
                {
                    resultCount = Int32.Parse(groups["list_count"].Value);
                }
                if (resultCount <= 0)
                {
                    resultCount = 1;
                }

                // Deal with the operation name
                Func <double, double, bool> compare;
                switch (opName)
                {
                case "MAX":
                    compare = delegate(double _new, double _old) { return(_new > _old); };
                    break;

                case "MIN":
                    compare = delegate(double _new, double _old) { return(_new < _old); };
                    break;

                default:
                    return(new ExpressionResult(false, $"{opName} is not a valid list operation", new double[] { 0 }));
                }

                // Parse the arguments
                string[] exprs = SplitBalanced(',', groups["list_inside"].Value);
                if (exprs.Length <= 0)
                {
                    return(new ExpressionResult(false, $"{opName} must have at least 1 value", new double[] { 0 }));
                }

                evaluated_string += $"{opName}{resultCount}[";

                // initialize comparison values:
                double[] compValues = new double[resultCount];
                int      initIndex  = 0;

                for (int i = 0; i < exprs.Length; i++)
                {
                    ExpressionResult result = Evaluate(exprs[i], charRef, variables);
                    if (!result.Success)
                    {
                        return(result);
                    }

                    if (i != 0)
                    {
                        evaluated_string += ", ";
                    }
                    evaluated_string += result.Message;

                    // set comparison values to the first item in the first expression's result
                    // finish comparison if needed
                    foreach (double newVal in result.Values)
                    {
                        // if our initial values aren't filled in, fill them first.
                        // no comparison is needed for the initial set.
                        if (initIndex < resultCount)
                        {
                            compValues[initIndex] = newVal;
                            initIndex++;
                            continue;
                        }

                        int    k      = 0;
                        double oldVal = 0;
                        for (; k < resultCount; k++)
                        {
                            // if we have a match, store the old value,
                            // plug in the new one, and break out of this loop
                            if (compare(newVal, compValues[k]))
                            {
                                oldVal        = compValues[k];
                                compValues[k] = newVal;
                                break;
                            }
                        }
                        // sift the old value down the rest of the results
                        for (; k < resultCount; k++)
                        {
                            if (compare(oldVal, compValues[k]))
                            {
                                compValues[k] = oldVal;
                            }
                        }
                    }
                }

                value             = compValues;
                evaluated_string += $"] ({String.Join(",",value)})";
            }

            else if (groups["func"].Value != "")
            {
                string funcName = groups["func"].Value;

                string[] funcArgs = SplitBalanced(',', groups["func_args"].Value);
                if (funcArgs[0] == "")
                {
                    funcArgs = new string[0];
                }
                for (int i = 0; i < funcArgs.Length; i++)
                {
                    funcArgs[i] = funcArgs[i].Trim();
                }

                if (charRef == null)
                {
                    return(new ExpressionResult(false, "No character given to reference", new double[] { 0 }));
                }
                else if (funcName == "EQUIPPED")
                {
                    if (charRef.HasItemEquipped(funcArgs))
                    {
                        value[0] = 1;
                    }
                    else
                    {
                        value[0] = 0;
                    }
                }
                else
                {
                    switch (funcName + funcArgs.Length.ToString())
                    {
                    case "HASPROF1":
                        if (charRef.HasProficiency(funcArgs[0]))
                        {
                            value[0] = 1;
                        }
                        else
                        {
                            value[0] = 0;
                        }
                        break;

                    case "ABILITYSCORE1":
                        value[0] = charRef.GetAbilityScore(funcArgs[0]);
                        break;

                    case "ABILITYMOD1":
                        value[0] = charRef.GetAbilityMod(funcArgs[0]);
                        break;

                    case "SKILLMOD1":
                        value[0] = charRef.GetSkillMod(funcArgs[0]);
                        break;

                    default:
                        return(new ExpressionResult(false, $"{funcName} with {funcArgs.Length} arguments is not a valid function", new double[] { 0 }));
                    }
                }
                evaluated_string += $"{funcName}({String.Join(",",funcArgs)}) ({value[0]})";
            }

            else
            {
                return(new ExpressionResult(false,
                                            $"Invalid Operator\n \"{expr}\"\n" +
                                            " " + underlineError(match), new double[] { 0 }));
            }

            // Handle preoperator
            if (groups["pre_op"].Value != "")
            {
                value             = calculate(groups["pre_op"].Value, value, new double[] { 0 });
                evaluated_string += $"({String.Join(",",value)})";
            }

            // Handle Operator

            switch (groups["op"].Value)
            {
            case "&&":
            case "AND":
            case "||":
            case "OR":
                opOrder = 0;
                break;

            case "<":
            case ">":
            case "=":
            case "<=":
            case ">=":
            case "!=":
                opOrder = 1;
                break;

            case "+":
            case "-":
                opOrder = 2;
                break;

            case "*":
            case "/":
                opOrder = 3;
                break;

            default:
                if (values.Count > 0)
                {
                    return(new ExpressionResult(false,
                                                $"Invalid Syntax\n \"{expr}\"\n" +
                                                " " + underlineError(match), new double[] { 5 }));
                }
                else
                {
                    values.Push(value);
                }
                break;
            }

            if (opOrder > -1)
            {
        #if (DEBUG)
                debugStack <double[]>(values);
                debugStack <(string, int)>(operators);
        #endif

                while (operators.Count > 0 && operators.Peek().Item2 > opOrder)
                {
                    values.Push(calculate(operators.Pop().Item1, values.Pop(), values.Pop()));
                }
                operators.Push((groups["op"].Value, opOrder));
                values.Push(value);
            }
        }

        // Check to make sure stacks are alligned properly. There should always be one more values than operators
        if (values.Count != operators.Count + 1)
        {
            return(new ExpressionResult(false, $"Invalid Expression\n \"{expr}\"", new double[] { 6 }));
        }

        // Compute remaining stack elements
        while (operators.Count > 0)
        {
      #if (DEBUG)
            debugStack <double[]>(values);
            debugStack <(string, int)>(operators);
      #endif

            values.Push(calculate(operators.Pop().Item1, values.Pop(), values.Pop()));
        }

        // The last remaining value is the answer
        double[] total = values.Pop();

    #if (DEBUG)
        Console.WriteLine(total);
    #endif

        return(new ExpressionResult(true, evaluated_string, total));
    }
示例#7
0
 private VariableDeclarationStatement ParseVariableDeclarationStatement(bool unsafeCode, VariableCollection variables)
 {
     bool constant = false;
     Symbol nextSymbol = this.GetNextSymbol();
     CsToken token = null;
     Microsoft.StyleCop.Node<CsToken> firstItemNode = null;
     if (nextSymbol.SymbolType == SymbolType.Const)
     {
         constant = true;
         token = new CsToken(nextSymbol.Text, CsTokenType.Const, nextSymbol.Location, this.symbols.Generated);
         firstItemNode = this.tokens.InsertLast(this.GetToken(CsTokenType.Const, SymbolType.Const));
         nextSymbol = this.GetNextSymbol();
     }
     if (nextSymbol.SymbolType != SymbolType.Other)
     {
         throw this.CreateSyntaxException();
     }
     LiteralExpression typeTokenExpression = this.GetTypeTokenExpression(unsafeCode, true);
     if ((typeTokenExpression == null) || (typeTokenExpression.Tokens.First == null))
     {
         throw new SyntaxException(this.document.SourceCode, token.LineNumber);
     }
     if (firstItemNode == null)
     {
         firstItemNode = typeTokenExpression.Tokens.First;
     }
     VariableDeclarationExpression expression = this.GetVariableDeclarationExpression(typeTokenExpression, ExpressionPrecedence.None, unsafeCode);
     this.tokens.Add(this.GetToken(CsTokenType.Semicolon, SymbolType.Semicolon));
     if (variables != null)
     {
         VariableModifiers modifiers = constant ? VariableModifiers.Const : VariableModifiers.None;
         foreach (VariableDeclaratorExpression expression3 in expression.Declarators)
         {
             Variable variable = new Variable(expression.Type, expression3.Identifier.Token.Text, modifiers, expression3.Tokens.First.Value.Location.StartPoint, expression.Tokens.First.Value.Generated || expression3.Identifier.Token.Generated);
             if (!variables.Contains(expression3.Identifier.Token.Text))
             {
                 variables.Add(variable);
             }
         }
     }
     return new VariableDeclarationStatement(new CsTokenList(this.tokens, firstItemNode, this.tokens.Last), constant, expression);
 }
示例#8
0
        public void SetConstantResults(VariableCollection optimized, VariableCollection reference)
        {
            if (optimized == null || reference == null)
            {
                throw new ArgumentNullException();
            }

            ConstantResults.Clear();
            foreach (var variable in reference.Where(variable => variable.StoreResult && !optimized.Contains(variable)))
            {
                ConstantResults.Add(variable.FullName, variable.Value);
            }
        }
        public void Primer()
        {
            //ExStart
            //ExFor:Document.Variables
            //ExFor:VariableCollection
            //ExFor:VariableCollection.Add
            //ExFor:VariableCollection.Clear
            //ExFor:VariableCollection.Contains
            //ExFor:VariableCollection.Count
            //ExFor:VariableCollection.GetEnumerator
            //ExFor:VariableCollection.IndexOfKey
            //ExFor:VariableCollection.Remove
            //ExFor:VariableCollection.RemoveAt
            //ExSummary:Shows how to work with a document's variable collection.
            Document           doc       = new Document();
            VariableCollection variables = doc.Variables;

            // Documents have a variable collection to which name/value pairs can be added
            variables.Add("Home address", "123 Main St.");
            variables.Add("City", "London");
            variables.Add("Bedrooms", "3");

            Assert.AreEqual(3, variables.Count);

            // Variables can be referenced and have their values presented in the document by DOCVARIABLE fields
            DocumentBuilder  builder = new DocumentBuilder(doc);
            FieldDocVariable field   = (FieldDocVariable)builder.InsertField(FieldType.FieldDocVariable, true);

            field.VariableName = "Home address";
            field.Update();

            Assert.AreEqual("123 Main St.", field.Result);

            // Assigning values to existing keys will update them
            variables.Add("Home address", "456 Queen St.");

            // DOCVARIABLE fields also need to be updated in order to show an accurate up to date value
            field.Update();

            Assert.AreEqual("456 Queen St.", field.Result);

            // The existence of variables can be looked up either by name or value like this
            Assert.True(variables.Contains("City"));
            Assert.True(variables.Any(v => v.Value == "London"));

            // Variables are automatically sorted in alphabetical order
            Assert.AreEqual(0, variables.IndexOfKey("Bedrooms"));
            Assert.AreEqual(1, variables.IndexOfKey("City"));
            Assert.AreEqual(2, variables.IndexOfKey("Home address"));

            // Enumerate over the collection of variables
            using (IEnumerator <KeyValuePair <string, string> > enumerator = doc.Variables.GetEnumerator())
                while (enumerator.MoveNext())
                {
                    Console.WriteLine($"Name: {enumerator.Current.Key}, Value: {enumerator.Current.Value}");
                }

            // Variables can be removed either by name or index, or the entire collection can be cleared at once
            variables.Remove("City");

            Assert.False(variables.Contains("City"));

            variables.RemoveAt(1);

            Assert.False(variables.Contains("Home address"));

            variables.Clear();

            Assert.That(variables, Is.Empty);
            //ExEnd
        }
示例#10
0
        public void Primer()
        {
            //ExStart
            //ExFor:Document.Variables
            //ExFor:VariableCollection
            //ExFor:VariableCollection.Add
            //ExFor:VariableCollection.Clear
            //ExFor:VariableCollection.Contains
            //ExFor:VariableCollection.Count
            //ExFor:VariableCollection.GetEnumerator
            //ExFor:VariableCollection.IndexOfKey
            //ExFor:VariableCollection.Remove
            //ExFor:VariableCollection.RemoveAt
            //ExSummary:Shows how to work with a document's variable collection.
            Document           doc       = new Document();
            VariableCollection variables = doc.Variables;

            // Every document has a collection of key/value pair variables, which we can add items to.
            variables.Add("Home address", "123 Main St.");
            variables.Add("City", "London");
            variables.Add("Bedrooms", "3");

            Assert.AreEqual(3, variables.Count);

            // We can display the values of variables in the document body using DOCVARIABLE fields.
            DocumentBuilder  builder = new DocumentBuilder(doc);
            FieldDocVariable field   = (FieldDocVariable)builder.InsertField(FieldType.FieldDocVariable, true);

            field.VariableName = "Home address";
            field.Update();

            Assert.AreEqual("123 Main St.", field.Result);

            // Assigning values to existing keys will update them.
            variables.Add("Home address", "456 Queen St.");

            // We will then have to update DOCVARIABLE fields to ensure they display an up-to-date value.
            Assert.AreEqual("123 Main St.", field.Result);

            field.Update();

            Assert.AreEqual("456 Queen St.", field.Result);

            // Verify that the document variables with a certain name or value exist.
            Assert.True(variables.Contains("City"));
            Assert.True(variables.Any(v => v.Value == "London"));

            // The collection of variables automatically sorts variables alphabetically by name.
            Assert.AreEqual(0, variables.IndexOfKey("Bedrooms"));
            Assert.AreEqual(1, variables.IndexOfKey("City"));
            Assert.AreEqual(2, variables.IndexOfKey("Home address"));

            // Enumerate over the collection of variables.
            using (IEnumerator <KeyValuePair <string, string> > enumerator = doc.Variables.GetEnumerator())
                while (enumerator.MoveNext())
                {
                    Console.WriteLine($"Name: {enumerator.Current.Key}, Value: {enumerator.Current.Value}");
                }

            // Below are three ways of removing document variables from a collection.
            // 1 -  By name:
            variables.Remove("City");

            Assert.False(variables.Contains("City"));

            // 2 -  By index:
            variables.RemoveAt(1);

            Assert.False(variables.Contains("Home address"));

            // 3 -  Clear the whole collection at once:
            variables.Clear();

            Assert.That(variables, Is.Empty);
            //ExEnd
        }