public void ConvertOperatorsWhitespacesReturnsMMLWithCorrectSpacing()
        {
            String latexExpression = @"$A,\qquad B, \quad C, \; D, \: E, \, F, \! G $";
            String latexToConvert  = begin + latexExpression + end;

            latex2MathMLConverter = new LatexMathToMathMLConverter();
            latex2MathMLConverter.BeforeXmlFormat += ConvertOperatorsWhitespacesReturnsMMLWithCorrectSpacingListener;
            latex2MathMLConverter.Convert(latexToConvert);

            String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""A,\qquad B, \quad C, \; D, \: E, \, F, \! G "" display=""inline"" class=""normalsize"">
<mstyle displaystyle=""true"" /><mrow>
<mi>A</mi>
<mo>,</mo>
<mspace width=""4em""/><mi>B</mi>
<mo>,</mo>
<mspace width=""2em""/><mi>C</mi>
<mo>,</mo>
<mspace width=""1.1em""/><mi>D</mi>
<mo>,</mo>
<mspace width=""0.9em""/><mi>E</mi>
<mo>,</mo>
<mspace width=""0.7em""/><mi>F</mi>
<mo>,</mo>
<mspace width=""0.3em""/><mi>G</mi>
</mrow>
</math>";

            Thread.Sleep(400);


            NUnit.Framework.Assert.That(result, Is.EqualTo(expected));
        }
        /// <summary>
        /// Parses the document and builds the document object tree.
        /// </summary>
        /// <param name="rdr">The reader to read the document from.</param>
        /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param>
        /// <returns>The root of the document object tree.</returns>
        public static LatexExpression CreateRoot(TextReader rdr, LatexMathToMathMLConverter customization)
        {
            bool verbatimMode = false;
            int  rootIndex    = 0;
            var  root         = new LatexExpression(null, 0, ref rootIndex, ref verbatimMode,
                                                    ExpressionType.Root, false, customization, null, null, null)
            {
                Expressions = new List <List <LatexExpression> >(1)
            };
            var             children  = new List <LatexExpression>();
            string          beginning = null;
            string          end;
            LatexExpression cmd;

            rootIndex = 0;
            bool mathMode         = false;
            bool whitespaceBefore = false;

            while ((cmd = ReadFromTextReader(root, 0, ref rootIndex, ref verbatimMode,
                                             mathMode, customization, beginning, rdr, out end, ref whitespaceBefore)) != null)
            {
                CheckMathMode(cmd, ref mathMode);
                beginning = end;
                children.Add(cmd);
            }
            root.Expressions.Add(children);
            return(root);
        }
        public void ConvertXSquaredReturnXSquaredInMML()
        {
            String latexExpression = "$x^2$";
            String latexToConvert  = begin + latexExpression + end;

            latex2MathMLConverter = new LatexMathToMathMLConverter();
            latex2MathMLConverter.BeforeXmlFormat += ConvertXSquaredReturnXSquaredInMMLEventListener;
            latex2MathMLConverter.Convert(latexToConvert);

            String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""x^2"" display=""inline"" class=""normalsize"">
<mstyle displaystyle=""true"" /><mrow>
<msup>
<mrow>
<mi>x</mi>
</mrow>
<mrow>
<mn>2</mn>
</mrow>
</msup>
</mrow>
</math>";

            Thread.Sleep(400);
            Console.WriteLine(result);
            Console.WriteLine(expected);
            NUnit.Framework.Assert.That(result, Is.EqualTo(expected));
        }
        public void ConvertSymbolsForAllInQuadExistsLeqEpsilonLeGeqGeReturnsCorrectMML()
        {
            String latexExpression = @"$\forall x \in X, \quad \exists y \leq \epsilon \le \geq \ge$";
            String latexToConvert  = begin + latexExpression + end;

            latex2MathMLConverter = new LatexMathToMathMLConverter();
            latex2MathMLConverter.BeforeXmlFormat += ConvertSymbolsForAllInQuadExistsLeqEpsilonReturnsCorrectMMLListener;
            latex2MathMLConverter.Convert(latexToConvert);

            String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""\forall x \in X, \quad \exists y \leq \epsilon \le \geq \ge"" display=""inline"" class=""normalsize"">
<mstyle displaystyle=""true"" /><mrow>
<mo>&#x2200;<!-- &forall; --></mo>
<mi>x</mi>
<mo>&#x2208;<!-- &isin; --></mo>
<mi>X</mi>
<mo>,</mo>
<mspace width=""2em""/><mo>&#x2203;<!-- &exist; --></mo>
<mi>y</mi>
<mo>&#8804; <!-- leq --> </mo> 
<mi>&#x3B5;<!-- &epsilon; --></mi><mo>&#8804; <!-- le --> </mo> 
<mo>&#8805; <!-- geq --> </mo> 
<mo>&#8805; <!-- ge --> </mo> 
</mrow>
</math>";

            Thread.Sleep(400);


            NUnit.Framework.Assert.That(result, Is.EqualTo(expected));
        }
        public void ConvertFracXYPlus2ReturnsFracXYPlus2InMML()
        {
            String latexExpression = @"$\frac{x}{y}+2$";
            String latexToConvert  = begin + latexExpression + end;

            latex2MathMLConverter = new LatexMathToMathMLConverter();
            latex2MathMLConverter.BeforeXmlFormat += ConvertFracXYPlus2ReturnsFracXYPlus2InMMLEventListener;
            latex2MathMLConverter.Convert(latexToConvert);
            String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""\frac{x}{y}+2"" display=""inline"" class=""normalsize"">
<mstyle displaystyle=""true"" /><mrow>
<mfrac>
<mrow>
<mi>x</mi>
</mrow>
<mrow>
<mi>y</mi>
</mrow>
</mfrac>
<mo>+</mo>
<mn>2</mn>
</mrow>
</math>";

            Thread.Sleep(400);

            NUnit.Framework.Assert.That(result, Is.EqualTo(expected));
        }
        public void ConvertOperatorsRelationShipOperatorsReturnsCorrectMML()
        {
            String latexExpression = @"$< <= \leq \ll \subset \subseteq \nsubseteq \sqsubset \sqsubseteq \preceq > >=\geq \gg \supset \supseteq \nsupseteq \sqsupset \sqsupseteq \succeq = \doteq \equiv \approx \cong \simeq \sim \propto \neq \parallel \asymp \vdash \in \smile \models \perp \prec \sphericalangle$";
            String latexToConvert  = begin + latexExpression + end;

            latex2MathMLConverter = new LatexMathToMathMLConverter();
            latex2MathMLConverter.BeforeXmlFormat += ConvertOperatorsRelationShipOperatorsReturnsCorrectMMLListener;
            latex2MathMLConverter.Convert(latexToConvert);

            String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""&amp;lt; &amp;lt;= \leq \ll \subset \subseteq \nsubseteq \sqsubset \sqsubseteq \preceq &amp;gt; &amp;gt;=\geq \gg \supset \supseteq \nsupseteq \sqsupset \sqsupseteq \succeq = \doteq \equiv \approx \cong \simeq \sim \propto \neq \parallel \asymp \vdash \in \smile \models \perp \prec \sphericalangle"" display=""inline"" class=""normalsize"">
<mstyle displaystyle=""true"" /><mrow>
<mo>&lt;</mo>
<mo>&le;</mo>
<mo>=</mo>
<mo>&#8804; <!-- leq --> </mo> 
<mo>&#x226A;<!-- &Lt; --></mo>
<mo>&#x2282;<!-- &sub; --></mo>
<mo>&#x2286;<!-- &sube; --></mo>
<mo>&#x2288;<!-- &nsube; --></mo>
<mo>&#x228F;<!-- &sqsub; --></mo>
<mo>&#x2291;<!-- &sqsube; --></mo>
<mo>&#x227C;<!-- &cupre; --></mo>
<mo>&gt;</mo>
<mo>&ge;</mo>
<mo>=</mo>
<mo>&#8805; <!-- geq --> </mo> 
<mo>&#x226B;<!-- &Gt; --></mo>
<mo>&#x2283;<!-- &sup; --></mo>
<mo>&#x2287;<!-- &supe; --></mo>
<mo>&#x2289;<!-- &nsupe; --></mo>
<mo>&#x2290;<!-- &sqsup; --></mo>
<mo>&#x2292;<!-- &sqsupe; --></mo>
<mo>&#x227D;<!-- &sccue; --></mo>
<mo>=</mo>
<mo>&#x2250;<!-- &esdot; --></mo>
<mo>&#x2261;<!-- equiv --></mo>
<mo>&#x2248;<!-- &asymp; --></mo>
<mo>&#x2245;<!-- &cong; --></mo>
<mo>&#x2243;<!-- &sime; --></mo>
<mo>&#x223C;<!-- &sim; --></mo>
<mo>&#x221D;<!-- &vprop; --></mo>
<mo>&#x2260;<!-- &ne; --></mo>
<mo>&#x20E6;</mo>
<mo>&#x224d;<!-- &asymp; --></mo>
<mo>&#x22A2;<!-- &vdash; --></mo>
<mo>&#x2208;<!-- &isin; --></mo>
<mo>&#x23DD;</mo>
<mo>&#x22A7;<!-- &models; --></mo>
<mo>&#x22A5;<!-- &bottom; --></mo>
<mo>&#x227A;<!-- &pr; --></mo>
<mo>&#x2222;<!-- &angsph; --></mo>
</mrow>
</math>";

            Thread.Sleep(400);


            NUnit.Framework.Assert.That(result, Is.EqualTo(expected));
        }
        /// <summary>
        /// Builds the document object tree.
        /// </summary>
        /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param>
        /// <remarks>The parsing procedure consists of stand-alone passes, so that it resembles a compiler pipeline.</remarks>
        public void Parse(LatexMathToMathMLConverter customization)
        {
            foreach (var rule in PreformatRules)
            {
                _source = _source.Replace(rule[0], rule[1]);
            }
            var        rdr        = new StringReader(_source);
            const byte PASS_COUNT = 14;
            byte       step       = 1;

            // Build the tree
            LogInfo("CreateRoot");
            _root = LatexExpression.CreateRoot(rdr, customization);
            OnProgressEvent(step++, PASS_COUNT);

            //TODO Go through the usage of _customCommands
            // Rebuild tree with custom commands
            _customCommands = new Dictionary <string, LatexExpression>();
            LogInfo("RecursiveParseCustomCommands");

            // Incapsulate fragments between \begin and \end
            LogInfo("IncapsulateCommands");
            IncapsulateCommands(Root.Expressions[0]);
            OnProgressEvent(step++, PASS_COUNT);

            // Post-parse arrays
            LogInfo("PostParseArrays");
            PostParseArrays(Root.Expressions[0], customization);
            OnProgressEvent(step++, PASS_COUNT);

            // Build super- and subscripts
            LogInfo("BuildScripts");
            BuildScripts(Root.Expressions[0], customization);
            OnProgressEvent(step++, PASS_COUNT);

            // Simplify math blocks that begin with baseless scripts
            LogInfo("SimplifyScripts");
            SimplifyBaselessScripts(Root.Expressions[0]);
            OnProgressEvent(step++, PASS_COUNT);

            // Numerate blocks (needed for the next step)
            LogInfo("NumerateBlocks");
            NumerateBlocks(Root.FindDocument().Expressions[0]);
            Root.Customization.Counters.Add("document", 0);
            OnProgressEvent(step++, PASS_COUNT);

            // Simplify math blocks that begin with baseless scripts
            LogInfo("PreProcessLabels");
            PreprocessLabels(Root.FindDocument().Expressions[0]);
            Root.Customization.Counters.Clear();
            OnProgressEvent(step++, PASS_COUNT);

            // Deal with algorithmic blocks
            LogInfo("PreprocessAlgorithms");
            PreprocessAlgorithms(Root.FindDocument().Expressions[0]);
            OnProgressEvent(step++, PASS_COUNT);

            LogInfo("Finished");
        }
 /// <summary>
 /// Initializes a new instance of the LatexParser class.
 /// </summary>
 /// <param name="source">The source string to build the tree from.</param>
 /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param>
 public LatexParser(string source, LatexMathToMathMLConverter customization)
 {
     if (String.IsNullOrEmpty(source))
     {
         throw new ArgumentException("The parameter can not be null or empty.", "source");
     }
     _source        = source;
     _customization = customization;
 }
 /// <summary>
 /// Appends the opening MathML &lt;math&gt; tag to the specified StringBuilder instance.
 /// </summary>
 /// <param name="bld">The StringBuilder instance.</param>
 /// <param name="altText">The alternative text.</param>
 /// <param name="inline">Indicates whether the math block is inline.</param>
 public static void AppendMathProlog(StringBuilder bld, string altText,
                                     bool inline, LatexMathToMathMLConverter customization)
 {
     bld.Append("<math xmlns=\"http://www.w3.org/1998/Math/MathML\" alttext=\"");
     bld.Append(altText);
     bld.Append("\" ");
     bld.Append(inline ? "display=\"inline\"" : "display=\"block\"");
     bld.Append(" class=\"" + "normalsize" + "\"");
     bld.Append(">\n<mstyle displaystyle=\"true\" />");
 }
        public static string ConvertOutline(IList <LatexExpression> outline, LatexMathToMathMLConverter customization)
        {
            var bld             = new StringBuilder();
            var backupTextSize  = customization.CurrentTextSize;
            var backupTextStyle = customization.CurrentTextStyle;

            foreach (var child in outline)
            {
                bld.Append(child.Convert());
            }
            customization.CurrentTextSize  = backupTextSize;
            customization.CurrentTextStyle = backupTextStyle;
            return(bld.ToString());
        }
 /// <summary>
 /// Initializes a new instance of the LatexExpression class of type ExpressionType.Block.
 /// </summary>
 /// <param name="name">The name of the block.</param>
 /// <param name="parent">The parent of the builded expression.</param>
 /// <param name="parentChildNumber">Index of the parent child outline.</param>
 /// <param name="indexInParentChild">Index in the parent child outline.</param>
 /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param>
 public LatexExpression(string name, LatexExpression parent, int parentChildNumber, int indexInParentChild, LatexMathToMathMLConverter customization)
 {
     Name          = name;
     Customization = customization;
     ExprType      = ExpressionType.Block;
     MathMode      = parent.MathMode |
                     parent.ExprType == ExpressionType.BlockMath | parent.ExprType == ExpressionType.InlineMath;
     Parent             = parent;
     ParentChildNumber  = parentChildNumber;
     IndexInParentChild = indexInParentChild;
     Expressions        = new List <List <LatexExpression> > {
         new List <LatexExpression>()
     };
 }
        /// <summary>
        /// Performs the conversion procedure of math blocks.
        /// </summary>
        /// <param name="outline">The sequence of expressions to convert.</param>
        /// <param name="altText">The alternative text.</param>
        /// <param name="inline">Indicates whether the math block is inline.</param>
        /// <returns>The converted XML string.</returns>
        private static string CommonConvert(IList <LatexExpression> outline, string altText,
                                            bool inline, LatexMathToMathMLConverter customization)
        {
            if (outline.Count == 0)
            {
                return("");
            }
            var bld = new StringBuilder();

            AppendMathProlog(bld, altText, inline, customization);
            bld.Append("<mrow>\n");
            bld.Append(SequenceConverter.ConvertOutline(outline, customization));
            bld.Append("</mrow>\n");
            AppendMathEpilog(bld);
            return(bld.ToString());
        }
 /// <summary>
 /// Initializes a new instance of the LatexExpression class and copies the specified expression data to it.
 /// </summary>
 /// <param name="expression">The expression to clone.</param>
 public LatexExpression(LatexExpression expression)
 {
     Parent        = expression.Parent;
     Name          = expression.Name;
     ExprType      = expression.ExprType;
     MathMode      = expression.MathMode;
     Customization = expression.Customization;
     if (expression.Options != null)
     {
         Options = new ExpressionOptions();
     }
     if (expression.Expressions != null)
     {
         Expressions = new List <List <LatexExpression> >(expression.Expressions);
     }
 }
        public void ConvertOperatorsSinCosReturnsSinCos()
        {
            String latexExpression = @"$\cos (2\theta) = \cos^2 \theta - \sin^2 \theta$";
            String latexToConvert  = begin + latexExpression + end;

            latex2MathMLConverter = new LatexMathToMathMLConverter();
            latex2MathMLConverter.BeforeXmlFormat += ConvertFracXYPlus2ReturnsFracXYPlus2InMMLEventListener;
            latex2MathMLConverter.Convert(latexToConvert);

            String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""\cos (2\theta) = \cos^2 \theta - \sin^2 \theta"" display=""inline"" class=""normalsize"">
<mstyle displaystyle=""true"" /><mrow>
<mi>cos</mi>
<mfenced>
<mrow>
<mn>2</mn>
<mi>&#x3B8;<!-- &theta; --></mi></mrow>
</mfenced>
<mo>=</mo>
<msup>
<mrow>
<mi>cos</mi>
</mrow>
<mrow>
<mn>2</mn>
</mrow>
</msup>
<mi>&#x3B8;<!-- &theta; --></mi><mo>-</mo>
<msup>
<mrow>
<mi>sin</mi>
</mrow>
<mrow>
<mn>2</mn>
</mrow>
</msup>
<mi>&#x3B8;<!-- &theta; --></mi></mrow>
</math>";

            Thread.Sleep(400);


            NUnit.Framework.Assert.That(result, Is.EqualTo(expected));
        }
        public void ConvertOperatorsDotsReturnsCorrectMML()
        {
            String latexExpression = @"$\dots \dotsm \vdots \ddots$";
            String latexToConvert  = begin + latexExpression + end;

            latex2MathMLConverter = new LatexMathToMathMLConverter();
            latex2MathMLConverter.BeforeXmlFormat += ConvertOperatorsDotsReturnsCorrectMMLListener;
            latex2MathMLConverter.Convert(latexToConvert);

            String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""\dots \dotsm \vdots \ddots"" display=""inline"" class=""normalsize"">
<mstyle displaystyle=""true"" /><mrow>
<mo>&hellip;</mo>
<mo>&ctdot;</mo>
<mo>&dtdot;</mo>
<mo>&vellip;</mo>
</mrow>
</math>";

            Thread.Sleep(400);


            NUnit.Framework.Assert.That(result, Is.EqualTo(expected));
        }
        public void ConvertOperatorsBinaryOperators()
        {
            String latexExpression = @"$\pm \mp \times \div \ast \star \dagger \ddagger \cap \cup \uplus \sqcap \sqcup \vee \wedge \cdot \diamond \bigtriangleup \bigtriangledown \triangleleft \triangledown \bigcirc \bullet \wr \oplus \ominus \otimes \oslash \odot \circ \setminus \amalg$";
            String latexToConvert  = begin + latexExpression + end;

            latex2MathMLConverter = new LatexMathToMathMLConverter();
            latex2MathMLConverter.BeforeXmlFormat += ConvertOperatorsBinaryOperatorsListener;
            latex2MathMLConverter.Convert(latexToConvert);

            String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""\dots \dotsm \vdots \ddots"" display=""inline"" class=""normalsize"">
<mstyle displaystyle=""true"" /><mrow>
<mo>&hellip;</mo>
<mo>&ctdot;</mo>
<mo>&dtdot;</mo>
<mo>&vellip;</mo>
</mrow>
</math>";

            Thread.Sleep(400);


            NUnit.Framework.Assert.That(result, Is.EqualTo(expected));
        }
        /// <summary>
        /// Builds a new LatexExpression instance (main).
        /// </summary>
        /// <param name="parent">The parent of the builded expression.</param>
        /// <param name="parentChildNumber">Index of the parent child outline.</param>
        /// <param name="indexInParentChild">Index in the parent child outline.</param>
        /// <param name="verbatimMode">True if verbatim mode is on; otherwise, false.</param>
        /// <param name="beginning">The beginning string.</param>
        /// <param name="rdr">The reader to read ahead.</param>
        /// <param name="mathMode">The math mode switch.</param>
        /// <param name="end">The stub of the unparsed part.</param>
        /// <param name="whitespaceBefore">Indicates whether there was at least one whitespace char before the returned result.</param>
        /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param>
        /// <returns>The parsed LatexExpression instance.</returns>
        private static LatexExpression ReadFromTextReaderInner(LatexExpression parent, int parentChildNumber,
                                                               ref int indexInParentChild, ref bool verbatimMode, bool mathMode, LatexMathToMathMLConverter customization,
                                                               string beginning, TextReader rdr, out string end, ref bool whitespaceBefore)
        {
            string name;

            #region Verbatim
            if (verbatimMode)
            {
                name = ReadToVerbatimEnd(beginning, rdr, out end);
                return(new LatexExpression(parent, parentChildNumber, ref indexInParentChild,
                                           ref verbatimMode, ExpressionType.Verbatim, false, customization, name, null, null));
            }
            #endregion

            bool   whitespaceBeforeGuard;
            string str = ReadToNotEmptyString(beginning, rdr, out whitespaceBeforeGuard);
            whitespaceBefore |= whitespaceBeforeGuard;
            if (str == null)
            {
                end = null;
                return(null);
            }
            string value;
            switch (str[0])
            {
            case '\\':
                #region Command
                string   options;
                string[] values;
                name = Regex.Match(str.Substring(1), @"^[a-zA-Z]+\*?").Value;
                if (String.IsNullOrEmpty(name))
                {
                    name    = "" + str[1];
                    options = null;
                    if (name != "[")
                    {
                        values = null;
                        end    = str.Substring(2);
                    }
                    else     // Math
                    {
                        value = ParseBraces(ref str, rdr, "\\[", "\\]", out whitespaceBefore);
                        end   = str;
                        return(new LatexExpression(parent, parentChildNumber, ref indexInParentChild,
                                                   ref verbatimMode, ExpressionType.BlockMath, mathMode, customization,
                                                   name, null, value));
                    }
                }
                else
                {
                    ParseCommandOptionsAndValues(
                        str.Substring(name.Length + 1), rdr, out end,
                        out options, out values, out whitespaceBefore);
                }
                return(new LatexExpression(parent, parentChildNumber, ref indexInParentChild, ref verbatimMode,
                                           ExpressionType.Command, mathMode, customization, name, options, values));

                #endregion
            case '$':
                #region Math
                ExpressionType type;
                if (str[1] == '$')
                {
                    name  = "$$";
                    value = ParseBraces(ref str, rdr, "$$", "$$", out whitespaceBefore);
                    type  = ExpressionType.BlockMath;
                }
                else
                {
                    name  = "$";
                    value = ParseBraces(ref str, rdr, "$", "$", out whitespaceBefore);
                    type  = ExpressionType.InlineMath;
                }
                end = str;
                return(new LatexExpression(parent, parentChildNumber, ref indexInParentChild,
                                           ref verbatimMode, type, false, customization, name, null, value));

                #endregion
            case '%':
                #region Comment
                end = null;
                return(new LatexExpression(parent, parentChildNumber, ref indexInParentChild, ref verbatimMode,
                                           ExpressionType.Comment, false, customization, str.Substring(1), null, null));

                #endregion
            case '{':
                #region Block
                value = ParseBraces(ref str, rdr, "{", "}", out whitespaceBefore);
                end   = str;
                return(new LatexExpression(parent, parentChildNumber, ref indexInParentChild,
                                           ref verbatimMode, ExpressionType.Block, mathMode, customization, "{}", null, value));

                #endregion
            case '&':
                #region Table cell
                end = str.Substring(1);
                return(new LatexExpression(parent, parentChildNumber, ref indexInParentChild, ref verbatimMode,
                                           ExpressionType.PlainText, mathMode, customization, "&", null, null));

                #endregion
            case '^':
            case '_':
                #region Block for sub and sup
                if (mathMode)
                {
                    name = "" + str[0];
                    str  = ReadToNotEmptyString(str.Substring(1), rdr, out whitespaceBefore);
                    switch (str[0])
                    {
                    case '{':
                        value = ParseBraces(ref str, rdr, "{", "}", out whitespaceBefore);
                        end   = str;
                        break;

                    case '\\':
                        var cmdName = Regex.Match(str.Substring(1), @"^[a-zA-Z]+\*?").Value;
                        ParseCommandOptionsAndValues(
                            str.Substring(cmdName.Length + 1), rdr, out end,
                            out options, out values, out whitespaceBefore);
                        value = "\\" + cmdName + options;
                        if (values != null)
                        {
                            for (int i = 0; i < values.Length; i++)
                            {
                                value += "{" + values[i] + "}";
                            }
                        }
                        break;

                    default:
                        value = "" + str[0];
                        if (char.IsDigit(str[0]))
                        {
                            value = Regex.Match(str, @"\d+").Value;
                        }
                        if (char.IsLetter(str[0]))
                        {
                            value = Regex.Match(str, @"[a-zA-Z]+").Value;
                        }
                        end = str.Substring(value.Length);
                        break;
                    }
                    return(new LatexExpression(parent, parentChildNumber, ref indexInParentChild,
                                               ref verbatimMode, ExpressionType.Block, true, customization, name, null, value));
                }
                goto default;

                #endregion
            default:
                #region Plain text block
                name = "";
                var stopChars = mathMode ? new[] { '\\', '$', '{', '%', '&', '^', '_' } :
                new[] { '\\', '$', '{', '%', '&' };
                int stopPos;
                while ((stopPos = GetStopPos(str, stopChars)) == -1)
                {
                    if (rdr != null)
                    {
                        name += str + " ";
                        str   = rdr.ReadLine();
                        if (str == null)     // End of the document
                        {
                            name = name.Substring(0, name.Length - 1);
                            break;
                        }
                        if (str.Trim() == "")
                        {
                            str     = "\\paragraph";
                            stopPos = 0;
                            break;
                        }
                    }
                    else
                    {
                        stopPos = str.Length;
                        break;
                    }
                }
                if (str == null)     // End of the document?
                {
                    end = null;
                }
                else
                {
                    name += str.Substring(0, stopPos);
                    if (stopPos < str.Length && (str[stopPos] == '^' || str[stopPos] == '_'))
                    {
                        var match = Regex.Match(name, @"[a-zA-Z]+\s*\Z");
                        var chunk = match.Value;
                        if (chunk.Trim() == "")
                        {
                            match = Regex.Match(name, @"\d+\s*\Z");
                            chunk = match.Value;
                            if (chunk.Trim() == "")
                            {
                                match = Regex.Match(name, @"\S\s*\Z");
                                chunk = match.Value.Trim();
                            }
                        }
                        if (name != chunk)
                        {
                            end  = name.Substring(match.Index) + str.Substring(stopPos);
                            name = name.Substring(0, match.Index);
                        }
                        else
                        {
                            end = str.Substring(stopPos);
                        }
                    }
                    else
                    {
                        end = str.Substring(stopPos);
                    }
                }
                return(new LatexExpression(parent, parentChildNumber, ref indexInParentChild, ref verbatimMode,
                                           ExpressionType.PlainText, mathMode, customization, (whitespaceBefore? " " : "") + SmartTextTrim(name), null, null));

                #endregion
            }
        }
 /// <summary>
 /// Builds a new LatexExpression instance (front).
 /// </summary>
 /// <param name="parent">The parent of the builded expression.</param>
 /// <param name="parentChildNumber">Index of the parent child outline.</param>
 /// <param name="indexInParentChild">Index in the parent child outline.</param>
 /// <param name="verbatimMode">True if verbatim mode is on; otherwise, false.</param>
 /// <param name="beginning">The beginning string.</param>
 /// <param name="rdr">The reader to read ahead.</param>
 /// <param name="end">The stub of the unparsed part.</param>
 /// <param name="mathMode">The math mode switch.</param>
 /// <param name="whitespaceBefore">Indicates whether there was at least one whitespace char before the returned result.</param>
 /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param>
 /// <returns></returns>
 private static LatexExpression ReadFromTextReader(LatexExpression parent, int parentChildNumber,
                                                   ref int indexInParentChild, ref bool verbatimMode, bool mathMode, LatexMathToMathMLConverter customization,
                                                   string beginning, TextReader rdr, out string end, ref bool whitespaceBefore)
 {
     if (beginning == null && rdr == null)
     {
         end = null;
         return(null);
     }
     return(ReadFromTextReaderInner(parent, parentChildNumber, ref indexInParentChild, ref verbatimMode,
                                    mathMode, customization, beginning ?? rdr.ReadLine(), rdr, out end, ref whitespaceBefore));
 }
        public void ConvertSymbolsGreekLettersReturnsCorrectMML()
        {
            String latexExpression = @"$A, \alpha, B, \beta, \Gamma, \gamma, \Delta, \delta, E, \epsilon, \varepsilon, Z, \zeta, H, \eta, \Theta, \theta, \vartheta, I, \iota, K, \kappa, \varkappa, \Lambda, \lambda, M, \mu, N, \nu, \Xi, \xi, O, o, \Pi, \pi, \varpi, P, \rho, \varrho, \Sigma, \sigma, \varsigma, T, \tau, \Upsilon, \upsilon, \Phi, \phi, \varphi, X, \chi, \Psi, \psi, \Omega, \omega$";
            String latexToConvert  = begin + latexExpression + end;

            latex2MathMLConverter = new LatexMathToMathMLConverter();
            latex2MathMLConverter.BeforeXmlFormat += ConvertSymbolsGreekLettersReturnsCorrectMMLListener;
            latex2MathMLConverter.Convert(latexToConvert);

            String expected = @"<math xmlns=""http://www.w3.org/1998/Math/MathML"" alttext=""A, \alpha, B, \beta, \Gamma, \gamma, \Delta, \delta, E, \epsilon, \varepsilon, Z, \zeta, H, \eta, \Theta, \theta, \vartheta, I, \iota, K, \kappa, \varkappa, \Lambda, \lambda, M, \mu, N, \nu, \Xi, \xi, O, o, \Pi, \pi, \varpi, P, \rho, \varrho, \Sigma, \sigma, \varsigma, T, \tau, \Upsilon, \upsilon, \Phi, \phi, \varphi, X, \chi, \Psi, \psi, \Omega, \omega"" display=""inline"" class=""normalsize"">
<mstyle displaystyle=""true"" /><mrow>
<mi>A</mi>
<mo>,</mo>
<mi>&#x3B1;<!-- &alpha; --></mi><mo>,</mo>
<mi>B</mi>
<mo>,</mo>
<mi>&#x3B2;<!-- &beta; --></mi><mo>,</mo>
<mi>&#x393;<!-- &Gamma; --></mi><mo>,</mo>
<mi>&#x3B3;<!-- &gamma; --></mi><mo>,</mo>
<mi>&#x394;<!-- &Delta; --></mi><mo>,</mo>
<mi>&#x3B4;<!-- &delta; --></mi><mo>,</mo>
<mi>E</mi>
<mo>,</mo>
<mi>&#x3B5;<!-- &epsilon; --></mi><mo>,</mo>
<mi>&#x3B5;<!-- &epsilon; --></mi><mo>,</mo>
<mi>Z</mi>
<mo>,</mo>
<mi>&#x3B6;<!-- &zeta; --></mi><mo>,</mo>
<mi>H</mi>
<mo>,</mo>
<mi>&#x3B7;<!-- &eta; --></mi><mo>,</mo>
<mi>&#x398;<!-- &Theta; --></mi><mo>,</mo>
<mi>&#x3B8;<!-- &theta; --></mi><mo>,</mo>
<mi>&#x03D1;<!-- &theta --></mi><mo>,</mo>
<mi>I</mi>
<mo>,</mo>
<mi>&#x3B9;<!-- &iota; --></mi><mo>,</mo>
<mi>K</mi>
<mo>,</mo>
<mi>&#x3BA;<!-- &kappa; --></mi><mo>,</mo>
<mi>&#x03F0;<!-- &kappav; --></mi><mo>,</mo>
<mi>&#x39b;<!-- &Lambda; --></mi><mo>,</mo>
<mi>&#x3BB;<!-- &lambda; --></mi><mo>,</mo>
<mi>M</mi>
<mo>,</mo>
<mi>&#x3BC;<!-- &mu; --></mi><mo>,</mo>
<mi>N</mi>
<mo>,</mo>
<mi>&#x3BD;<!-- &nu; --></mi><mo>,</mo>
<mi>&#x39e;<!-- &Xi; --></mi><mo>,</mo>
<mi>&#x3BE;<!-- &xi; --></mi><mo>,</mo>
<mi>O</mi>
<mo>,</mo>
<mi>o</mi>
<mo>,</mo>
<mi>&#x3a0;<!-- &Pi; --></mi><mo>,</mo>
<mi>&#x3C0;<!-- &pi; --></mi><mo>,</mo>
<mi>&#x03D6;<!-- &piv; --></mi><mo>,</mo>
<mi>P</mi>
<mo>,</mo>
<mi>&#x3C1;<!-- &rho; --></mi><mo>,</mo>
<mi>&#x03F1;<!--&rhov --></mi><mo>,</mo>
<mi>&#x3a3;<!--&Sigma --></mi><mo>,</mo>
<mi>&#x3C3;<!-- &sigma; --></mi><mo>,</mo>
<mi>&#x03C2;<!--&sigmav; --></mi><mo>,</mo>
<mi>T</mi>
<mo>,</mo>
<mi>&#x3C4;<!-- &tau; --></mi><mo>,</mo>
<mi>&#x3a5;<!-- &Upsilon; --></mi><mo>,</mo>
<mi>&#x3C5;<!-- &upsilon; --></mi><mo>,</mo>
<mi>&#x3a6;<!-- &Phi; --></mi><mo>,</mo>
<mi>&#x3c6;<!-- &phi; --></mi><mo>,</mo>
<mi>&#x03D5;<!-- &phiv; --></mi><mo>,</mo>
<mi>X</mi>
<mo>,</mo>
<mi>&#x3C7;<!-- &chi; --></mi><mo>,</mo>
<mi>&#x3a8;<!-- &Psi; --></mi><mo>,</mo>
<mi>&#x3C8;<!-- &psi; --></mi><mo>,</mo>
<mi>&#x3a9;<!-- &Omega; --></mi><mo>,</mo>
<mi>&#x3C9;<!-- &omega; --></mi></mrow>
</math>";

            Thread.Sleep(400);


            NUnit.Framework.Assert.That(result, Is.EqualTo(expected));
        }
        /// <summary>
        /// Initializes a new instance of the LatexExpression class.
        /// </summary>
        /// <param name="parent">The parent of the builded expression.</param>
        /// <param name="parentChildNumber">Index of the parent child outline.</param>
        /// <param name="indexInParentChild">Index in the parent child outline.</param>
        /// <param name="verbatimMode">True if verbatim mode is on; otherwise, false.</param>
        /// <param name="type">The expression type.</param>
        /// <param name="mathMode">The math mode switch.</param>
        /// <param name="name">The expression name.</param>
        /// <param name="options">The options of the expression.</param>
        /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param>
        /// <param name="values">The child outlines of the expression.</param>
        private LatexExpression(LatexExpression parent, int parentChildNumber, ref int indexInParentChild,
                                ref bool verbatimMode, ExpressionType type, bool mathMode, LatexMathToMathMLConverter customization,
                                string name, string options, params string[] values)
        {
            Parent             = parent;
            ParentChildNumber  = parentChildNumber;
            IndexInParentChild = indexInParentChild++;
            Customization      = customization;
            Name     = name;
            ExprType = type;
            MathMode = mathMode;
            #region Switch verbatim mode on/off
            if (type == ExpressionType.Verbatim)
            {
                verbatimMode = false;
            }
            if (Expressions != null && Name == "begin" &&
                Expressions[0][0].Name == "verbatim" && Expressions.Count == 1)
            {
                verbatimMode = true;
            }
            #endregion
            ParseOptions(options);
            if (values != null)
            {
                ParseExpressions(values);
            }
            #region If a math block, add the alternative text
            if (type == ExpressionType.InlineMath || type == ExpressionType.BlockMath)
            {
                // ReSharper disable PossibleNullReferenceException
                Expressions.Add(new List <LatexExpression>());
                int index = 0;
                Expressions[1].Add(new LatexExpression(this, 1, ref index, ref verbatimMode,
                                                       ExpressionType.PlainText, false, Customization, values[0], null, null));
                // ReSharper restore PossibleNullReferenceException
            }
            #endregion
            #region Post-parse plain text if in math mode
            if (ExprType == ExpressionType.PlainText && mathMode && Name.Length > 1 &&
                (Parent.ExprType != ExpressionType.Block || Parent.Name != "PlainText") &&
                (Parent.ExprType != ExpressionType.Command || Parent.Name != "begin" && Parent.Name != "end"))
            {
                ExprType = ExpressionType.Block;
                Name     = "PlainText";
                #region Parse
                var list = new List <string>();
                var buf  = "" + name[0];
                for (int pos = 1; pos < name.Length; pos++)
                {
                    var chrPre = name[pos - 1];
                    var chr    = name[pos];
                    if (char.IsWhiteSpace(chr))
                    {
                        list.Add(buf);
                        buf = "";
                        continue;
                    }
                    if (char.IsDigit(chr))
                    {
                        #region Digit
                        if (char.IsDigit(chrPre))
                        {
                            buf += chr;
                            continue;
                        }
                        if (char.IsLetter(chr) || chr == '(')
                        {
                            list.Add("InvisibleTimes");
                        }
                        list.Add(buf);
                        buf = "" + chr;
                        continue;

                        #endregion
                    }
                    if (char.IsLetter(chr))
                    {
                        #region Letter
                        if (char.IsLetter(chrPre))
                        {
                            buf += chr;
                            continue;
                        }
                        if (char.IsDigit(chr) || chr == '(')
                        {
                            list.Add("InvisibleTimes");
                        }
                        list.Add(buf);
                        buf = "" + chr;
                        continue;

                        #endregion
                    }
                    #region >=, <=
                    if (chr == '=')
                    {
                        if (chrPre == '>' || chrPre == '<')
                        {
                            buf += chr;
                        }
                    }
                    #endregion
                    list.Add(buf);
                    buf = "" + chr;
                }
                list.Add(buf);
                #endregion
                Expressions = new List <List <LatexExpression> >(list.Count)
                {
                    new List <LatexExpression>()
                };
                int i;
                for (i = 0; i < list.Count;)
                {
                    if (list[i].Trim() != "")
                    {
                        Expressions[0].Add(new LatexExpression(this, 0, ref i, ref verbatimMode,
                                                               ExpressionType.PlainText, true, customization, list[i].Trim(), null, null));
                    }
                    else
                    {
                        i++;
                    }
                }
            }
            #endregion
        }
 /// <summary>
 /// Post-parses arrays.
 /// </summary>
 /// <param name="outline">The outline of a LatexExpression instance.</param>
 /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param>
 private static void PostParseArrays(IList <LatexExpression> outline, LatexMathToMathMLConverter customization)
 {
     for (int i = 0; i < outline.Count; i++)
     {
         if (outline[i].ExprType == ExpressionType.Block &&
             (ArrayLikeBlockNames.Contains(outline[i].Name)))
         {
             int parentChildNumber = outline[i].Expressions.Count == 1? 0 : 1;
             var main        = outline[i].Expressions[parentChildNumber];
             var latex_array = new List <List <List <LatexExpression> > >(3)
             {
                 new List <List <LatexExpression> >(3)
             };
             #region Build array
             int rowIndex = 0;
             for (int j = 0; ; j++)
             {
                 var cell = new List <LatexExpression>(2);
                 for (; j < main.Count &&
                      !(main[j].Name == "\\" && main[j].ExprType == ExpressionType.Command) &&
                      !(main[j].Name == "&" && main[j].ExprType == ExpressionType.PlainText)
                      ; j++)
                 {
                     // \hline won't do the same in XHTML
                     if (main[j].ExprType == ExpressionType.Command && main[j].Name == "hline")
                     {
                         continue;
                     }
                     if (main[j].ExprType == ExpressionType.Comment)
                     {
                         continue;
                     }
                     cell.Add(main[j]);
                 }
                 if (cell.Count > 0)
                 {
                     latex_array[rowIndex].Add(cell);
                 }
                 if (j == main.Count)
                 {
                     break;
                 }
                 if (main[j].Name == "\\")
                 {
                     rowIndex++;
                     latex_array.Add(new List <List <LatexExpression> >(3));
                 }
             }
             if (latex_array[rowIndex].Count == 0)
             {
                 latex_array.RemoveAt(rowIndex);
             }
             #endregion
             #region Link array
             main = new List <LatexExpression>(latex_array.Count);
             for (int j = 0; j < latex_array.Count; j++)
             {
                 // Add row
                 main.Add(new LatexExpression("", outline[i], parentChildNumber, j, customization));
                 for (int k = 0; k < latex_array[j].Count; k++)
                 {
                     // Add column cell
                     main[j].Expressions[0].Add(new LatexExpression("", main[j], 0, k, customization));
                     for (int m = 0; m < latex_array[j][k].Count; m++)
                     {
                         latex_array[j][k][m].Parent             = main[j].Expressions[0][k];
                         latex_array[j][k][m].ParentChildNumber  = 0;
                         latex_array[j][k][m].IndexInParentChild = m;
                         // Add cell atom
                         main[j].Expressions[0][k].Expressions[0].Add(latex_array[j][k][m]);
                     }
                 }
             }
             outline[i].Expressions[parentChildNumber] = main;
             #endregion
         }
     }
     for (int i = 0; i < outline.Count; i++)
     {
         if (outline[i].Expressions != null)
         {
             foreach (var subTree in outline[i].Expressions)
             {
                 PostParseArrays(subTree, customization);
             }
         }
     }
 }
 /// <summary>
 /// Recursively build scripts (for msup, munder, etc.)
 /// </summary>
 /// <param name="list">The outline of a LatexExpression instance.</param>
 /// <param name="customization">The LatexMathToMathMLConverter class instance to customize the conversion result.</param>
 private static void BuildScripts(List <LatexExpression> list, LatexMathToMathMLConverter customization)
 {
     for (int i = 0; i < list.Count; i++)
     {
         if (list[i].Expressions != null)
         {
             foreach (var subTree in list[i].Expressions)
             {
                 BuildScripts(subTree, customization);
             }
         }
         if (list[i].ExprType == ExpressionType.Block &&
             (list[i].Name == "^" || list[i].Name == "_") && i > 0)
         {
             if (list[i - 1].ExprType == ExpressionType.Command && list[i - 1].Name == "limits")
             {
                 list[i - 1].EraseFromParent();
                 i--;
             }
             #region Place the previous expression to the script block
             if (i < list.Count - 1 && list[i + 1].ExprType == ExpressionType.Block &&
                 (list[i + 1].Name == "^" || list[i + 1].Name == "_"))
             {
                 var block = new LatexExpression("script" + list[i].Name + list[i + 1].Name, list[i].Parent,
                                                 list[i].ParentChildNumber, i - 1, customization);
                 block.MathMode = list[i].MathMode;
                 block.Expressions[0].Add(new LatexExpression(list[i - 1]));
                 block.Expressions[0].Add(new LatexExpression(list[i]));
                 block.Expressions[0].Add(new LatexExpression(list[i + 1]));
                 block.Expressions[0][0].Parent             = block;
                 block.Expressions[0][0].ParentChildNumber  = 0;
                 block.Expressions[0][0].IndexInParentChild = 0;
                 block.Expressions[0][0].MathMode           = block.MathMode;
                 block.Expressions[0][1].Parent             = block;
                 block.Expressions[0][1].ParentChildNumber  = 0;
                 block.Expressions[0][1].IndexInParentChild = 1;
                 block.Expressions[0][1].MathMode           = block.MathMode;
                 block.Expressions[0][2].Parent             = block;
                 block.Expressions[0][2].ParentChildNumber  = 0;
                 block.Expressions[0][2].IndexInParentChild = 2;
                 block.Expressions[0][2].MathMode           = block.MathMode;
                 list[i - 1] = block;
                 list[i].EraseFromParent();
                 list[i].EraseFromParent();
             }
             else
             {
                 var block = new LatexExpression("script" + list[i].Name, list[i].Parent, list[i].ParentChildNumber, i - 1, customization);
                 block.MathMode = list[i].MathMode;
                 block.Expressions[0].Add(new LatexExpression(list[i - 1]));
                 block.Expressions[0].Add(new LatexExpression(list[i]));
                 block.Expressions[0][0].Parent             = block;
                 block.Expressions[0][0].ParentChildNumber  = 0;
                 block.Expressions[0][0].IndexInParentChild = 0;
                 block.Expressions[0][0].MathMode           = block.MathMode;
                 block.Expressions[0][1].Parent             = block;
                 block.Expressions[0][1].ParentChildNumber  = 0;
                 block.Expressions[0][1].IndexInParentChild = 1;
                 block.Expressions[0][1].MathMode           = block.MathMode;
                 list[i - 1] = block;
                 list[i].EraseFromParent();
             }
             i--;
             #endregion
         }
     }
 }