Exemple #1
0
        /// <summary>
        ///   Create a rule from the specified <see cref="XPathNavigator"/>.
        /// </summary>
        /// <param name="xml">
        ///   The XML representation of a rule based number format.
        /// </param>
        /// <returns>
        ///   A new rule.
        /// </returns>
        /// <remarks>
        ///   The <paramref name="xml"/> must be on an "rbnfrule" element.
        /// </remarks>
        public static IRule Parse(XPathNavigator xml)
        {
            Rule rule        = null;
            var  value       = xml.GetAttribute("value", "");
            var  radix       = xml.GetAttribute("radix", "");
            int  radixNumber = radix.Length == 0
                ? 10
                : Int32.Parse(radix, CultureInfo.InvariantCulture);

            switch (value)
            {
            case "-x":
                rule = new NegativeNumberRule();
                break;

            case "Inf":
                rule = new InfinityRule();
                break;

            case "NaN":
                rule = new NanRule();
                break;

            case "x.x":
                rule = new ImproperFractionRule();
                break;

            case "0.x":
                rule = new ProperFractionRule();
                break;

            case "x.0":
                rule = new MasterRule();
                break;

            default:
                var c = value[0];
                if ('0' <= c && c <= '9')
                {
                    rule = new BaseValueRule
                    {
                        LowerLimit = decimal.Parse(value),
                        radix      = radixNumber
                    };
                }
                else
                {
                    throw new FormatException($"Unknown rule value '{value}'.");
                }
                break;
            }

            var body = xml.Value;

            rule.Substitutions = Substitution.Parse(body.TrimEnd(';')).ToArray();

            return(rule);
        }
Exemple #2
0
        public void TextOnly()
        {
            var subs = Substitution.Parse("one").ToArray();

            Assert.AreEqual(1, subs.Length);
            Assert.AreEqual("", subs[0].Descriptor);
            Assert.AreEqual("one", subs[0].Text);
            Assert.AreEqual("", subs[0].Token);
            Assert.AreEqual(null, subs[0].Optionals);
        }
Exemple #3
0
        public void NumberPattern()
        {
            var subs = Substitution.Parse("=#,##0=").ToArray();

            Assert.AreEqual(1, subs.Length);

            Assert.AreEqual("#,##0", subs[0].Descriptor);
            Assert.AreEqual("", subs[0].Text);
            Assert.AreEqual("=", subs[0].Token);
            Assert.AreEqual(null, subs[0].Optionals);
        }
Exemple #4
0
        public void Text_Recurse_GT2()
        {
            var subs = Substitution.Parse("-→→").ToArray();

            Assert.AreEqual(2, subs.Length);

            Assert.AreEqual("", subs[0].Descriptor);
            Assert.AreEqual("-", subs[0].Text);
            Assert.AreEqual("", subs[0].Token);
            Assert.AreEqual(null, subs[0].Optionals);

            Assert.AreEqual("", subs[1].Descriptor);
            Assert.AreEqual("", subs[1].Text);
            Assert.AreEqual("→→", subs[1].Token);
            Assert.AreEqual(null, subs[1].Optionals);
        }
Exemple #5
0
        public void Text_Recurse_LT()
        {
            var subs = Substitution.Parse("←← hundred").ToArray();

            Assert.AreEqual(2, subs.Length);

            Assert.AreEqual("", subs[0].Descriptor);
            Assert.AreEqual("", subs[0].Text);
            Assert.AreEqual("←←", subs[0].Token);
            Assert.AreEqual(null, subs[0].Optionals);

            Assert.AreEqual("", subs[1].Descriptor);
            Assert.AreEqual(" hundred", subs[1].Text);
            Assert.AreEqual("", subs[1].Token);
            Assert.AreEqual(null, subs[1].Optionals);
        }
Exemple #6
0
        public void Text_Transfer_to_Rule()
        {
            var subs = Substitution.Parse("e-=%%et-unieme=").ToArray();

            Assert.AreEqual(2, subs.Length);

            Assert.AreEqual("", subs[0].Descriptor);
            Assert.AreEqual("e-", subs[0].Text);
            Assert.AreEqual("", subs[0].Token);
            Assert.AreEqual(null, subs[0].Optionals);

            Assert.AreEqual("et-unieme", subs[1].Descriptor);
            Assert.AreEqual("", subs[1].Text);
            Assert.AreEqual("=", subs[1].Token);
            Assert.AreEqual(null, subs[1].Optionals);
        }
Exemple #7
0
        void Apply(Substitution sub, RbnfContext context)
        {
            var number = context.Number;

            context.Text.Append(sub.Text);
            switch (sub.Token)
            {
            case "→→":
                context.Number = Decimal.Remainder(number, Divisor());
                context.Ruleset.ApplyRules(context);
                break;

            case "←←":
                context.Number = Math.Floor(number / Divisor());
                context.Ruleset.ApplyRules(context);
                break;

            case "=":
                // Fallback number formating?
                if (sub.Descriptor.StartsWith("#") || sub.Descriptor.StartsWith("0"))
                {
                    context.Text.Append(context.Number.ToString(sub.Descriptor, CultureInfo.InvariantCulture));
                }

                // Else goto the ruleset.
                else
                {
                    var ruleset = context.RulesetGroup.Rulesets[sub.Descriptor];
                    ruleset.ApplyRules(context);
                }
                break;

            case "":
                break;

            default:
                throw new NotSupportedException($"Substitution token '{sub.Token}' is not allowed.");
            }

            if (sub.Optional != null && (number % Divisor()) != 0)
            {
                Apply(sub.Optional, context);
            }

            context.Number = number;
        }
Exemple #8
0
        public void Text_CallRule1()
        {
            var subs = Substitution.Parse("←← cent→%%cents-m→").ToArray();

            Assert.AreEqual(3, subs.Length);

            Assert.AreEqual("", subs[0].Descriptor);
            Assert.AreEqual("", subs[0].Text);
            Assert.AreEqual("←←", subs[0].Token);
            Assert.AreEqual(null, subs[0].Optionals);

            Assert.AreEqual("", subs[1].Descriptor);
            Assert.AreEqual(" cent", subs[1].Text);
            Assert.AreEqual("", subs[1].Token);
            Assert.AreEqual(null, subs[1].Optionals);

            Assert.AreEqual("cents-m", subs[2].Descriptor);
            Assert.AreEqual("", subs[2].Text);
            Assert.AreEqual("→", subs[2].Token);
            Assert.AreEqual(null, subs[2].Optionals);
        }
Exemple #9
0
        /// <summary>
        ///   Parses a rule body.
        /// </summary>
        /// <param name="s">
        ///   The text representation of action(s) to perform.
        /// </param>
        /// <returns>
        ///   A sequence of actions to perform.
        /// </returns>
        public static IEnumerable <Substitution> Parse(string s)
        {
            // If a rule body begins with an apostrophe, the apostrophe is ignored,
            // but all text after it becomes significant (this is how you can
            // have a rule's rule text begin with whitespace).
            if (s.StartsWith("'"))
            {
                s = s.Substring(1);
            }

            for (var match = SubstitutionRegex.Match(s); match.Success; match = match.NextMatch())
            {
                var substitution = new Substitution
                {
                    Token      = match.Groups["token"].Value,
                    Text       = match.Groups["text"].Value,
                    Descriptor = ""
                };

                if (match.Groups["desc"].Success)
                {
                    var desc = match.Groups["desc"].Value;
                    substitution.Token = desc.Substring(0, 1);
                    desc = desc.Substring(1, desc.Length - 2);
                    substitution.Descriptor = desc
                                              .TrimStart('%');
                }

                if (match.Groups["opt"].Success)
                {
                    var opt = match.Groups["opt"].Value;
                    substitution.Optionals = Parse(opt).ToArray();
                }

                // Ignore empty matches; everything is optional in the regex.
                //if (!string.IsNullOrEmpty(substitution.Text) || !string.IsNullOrEmpty(substitution.Token))
                yield return(substitution);
            }
        }
Exemple #10
0
        public static IEnumerable <Substitution> Parse(string s)
        {
            for (var match = SubstitutionRegex.Match(s); match.Success; match = match.NextMatch())
            {
                var substitution = new Substitution
                {
                    Token      = match.Groups["token"].Value,
                    Text       = match.Groups["text"].Value,
                    Descriptor = match.Groups["desc"].Value
                };

                if (match.Groups["opt"].Success)
                {
                    substitution.Optional = Parse(match.Groups["opt"].Value).First();
                }

                // Ignore empty matches; everything is optional in the regex.
                if (!string.IsNullOrEmpty(substitution.Text) || !string.IsNullOrEmpty(substitution.Token))
                {
                    yield return(substitution);
                }
            }
        }
Exemple #11
0
        void Apply(Substitution sub, RbnfContext context)
        {
            var number = context.Number;

            context.Text.Append(sub.Text);
            switch (sub.Token)
            {
            // Divide the number by the rule's divisor and format the remainder.
            case "→→":
                context.Number = Decimal.Remainder(number, Divisor());
                context.Ruleset.ApplyRules(context);
                break;

            // Divide the number by the rule's divisor and format the quotient.
            case "←←":
                context.Number = Math.Floor(number / Divisor());
                context.Ruleset.ApplyRules(context);
                break;

            case "=":
                // Fallback number formating?
                if (sub.Descriptor.StartsWith("#") || sub.Descriptor.StartsWith("0"))
                {
                    var formatter = NumberFormatter.Create(context.Locale);
                    context.Text.Append(formatter.Format(context.Number));
                }

                // Else goto the ruleset.
                else
                {
                    var ruleset = context.RulesetGroup.Rulesets[sub.Descriptor];
                    ruleset.ApplyRules(context);
                }
                break;

            // Divide the number by the rule's divisor and format the remainder
            // with the specified ruleset.
            case "→":
                context.Number = Decimal.Remainder(number, Divisor());
                context.RulesetGroup
                .Rulesets[sub.Descriptor]
                .ApplyRules(context);
                break;

            // Divide the number by the rule's divisor and format the quotient
            // with the specified ruleset.
            case "←":
                context.Number = Math.Floor(number / Divisor());
                context.RulesetGroup
                .Rulesets[sub.Descriptor]
                .ApplyRules(context);
                break;

            case "":
                break;

            default:
                throw new NotSupportedException($"Substitution token '{sub.Token}' is not allowed.");
            }

            if (sub.Optionals != null && (number % Divisor()) != 0)
            {
                foreach (var optional in sub.Optionals)
                {
                    Apply(optional, context);
                }
            }

            context.Number = number;
        }