/// <summary>
        /// Validates and returns a fact.
        ///
        /// Throws an exception if the id is not in Facts.cs or if the
        /// value cannot be parsed to the type specified in Facts.cs
        ///
        /// </summary>
        /// <param name="id"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private static Fact ParseFact(string id, string value)
        {
            var fact = new Fact()
            {
                Id = id
            };

            // Validate id (must be defined in Facts.cs)
            var valueType = Facts.GetType(id);

            // Validate value (must be parsable to type specified in Facts)
            if (valueType == typeof(string))
            {
                fact.Value = new StringValue(value);
                if (fact.Value.GetValue() == null)
                {
                    throw new RulesException("Failed to parse string value");
                }
            }
            else if (valueType == typeof(int))
            {
                fact.Value = new IntValue(value);
                if (fact.Value.GetValue() == null)
                {
                    throw new RulesException("Failed to parse int value");
                }
            }
            else if (valueType == typeof(decimal))
            {
                fact.Value = new DecimalValue(value);
                if (fact.Value.GetValue() == null)
                {
                    throw new RulesException("Failed to parse decimal value");
                }
            }
            else if (valueType == typeof(bool))
            {
                fact.Value = new BoolValue(value);
                if (fact.Value.GetValue() == null)
                {
                    throw new RulesException("Failed to parse bool value");
                }
            }
            else
            {
                throw new RulesException("Don't know how to parse value");
            }

            return(fact);
        }
        private static Condition ReadCondition(YamlNode node)
        {
            // Read condition
            var yamlCondition = GetYamlString(node);

            if (yamlCondition == null)
            {
                throw new RulesException($"Expected a condition (string) but found: {node}");
            }

            Condition.Oper?oper      = null;
            var            condition = new Condition();

            // Parse condition (allow 3rd token to contain spaces)
            var tokens = yamlCondition.Split(" ", 3);

            if (tokens.Length == 1)
            {
                // Special prefixes & (IsDefined) and ! (NotDefined)
                if (tokens[0][0] == '&' || tokens[0][0] == '!')
                {
                    oper         = tokens[0][0] == '&' ? Condition.Oper.IsDefined : Condition.Oper.NotDefined;
                    condition.Id = tokens[0].Substring(1);

                    // Validate id (must be defined in Facts.cs)
                    Facts.GetType(condition.Id);
                }
                else
                {
                    // Boolean Combiners (And/Or)
                    var boolOp = tokens[0].ToLower();
                    if (boolOp == "and")
                    {
                        oper = Condition.Oper.And;
                    }
                    else if (boolOp == "or")
                    {
                        oper = Condition.Oper.Or;
                    }
                }
            }
            else if (tokens.Length == 3)
            {
                switch (tokens[1])
                {
                case "==":
                    oper = Condition.Oper.Equal;
                    break;

                case "!=":
                    oper = Condition.Oper.NotEqual;
                    break;

                case "<":
                    oper = Condition.Oper.LessThan;
                    break;

                case ">":
                    oper = Condition.Oper.GreaterThan;
                    break;

                case "<=":
                    oper = Condition.Oper.LessThanOrEqual;
                    break;

                case ">=":
                    oper = Condition.Oper.GreaterThanOrEqual;
                    break;

                default:
                    break;
                }

                try
                {
                    var fact = ParseFact(tokens[0], tokens[2]);
                    condition.Id    = fact.Id;
                    condition.Value = fact.Value;
                }
                catch (RulesException ex)
                {
                    throw new RulesException($"{ex.Message} in condition: {yamlCondition}");
                }
            }

            if (oper == null)
            {
                throw new RulesException($"Expected condition in format: 'AND', 'OR', '!id', 'id [==|!=|<|>|<=|>=] value' but found: {yamlCondition}");
            }

            condition.Op = oper ?? Condition.Oper.Equal;

            return(condition);
        }