Example #1
0
        private void RunScriptTest(string input, Func <IMathAtom, IMathList> scriptGetter, MathAtomType[][] atomTypes, string output)
        {
            var builder = new MathListBuilder(input);
            var list    = builder.Build();

            Assert.Null(builder.Error);
            ExpandGroups(list);
            CheckAtomTypes(list, atomTypes[0]);

            IMathAtom firstAtom = list[0];
            var       types     = atomTypes[1];

            if (types.Count() > 0)
            {
                Assert.NotNull(scriptGetter(firstAtom));
            }
            var scriptList = scriptGetter(firstAtom);

            CheckAtomTypes(scriptList, atomTypes[1]);
            if (atomTypes.Count() == 3)
            {
                // one more level
                var firstScript      = scriptList[0];
                var scriptScriptList = scriptGetter(firstScript);
                CheckAtomTypes(scriptScriptList, atomTypes[2]);
            }

            string latex = MathListBuilder.MathListToString(list);

            Assert.Equal(output, latex);
        }
Example #2
0
 static void InsertAtAtomIndexAndAdvance(this IMathList self, int atomIndex, IMathAtom atom, ref MathListIndex advance, MathListSubIndexType advanceType)
 {
     if (atomIndex < 0 || atomIndex > self.Count)
     {
         throw new IndexOutOfRangeException($"Index {atomIndex} is out of bounds for list of size {self.Atoms.Count}");
     }
     // Test for placeholder to the right of index, e.g. \sqrt{‸■} -> \sqrt{2‸}
     if (atomIndex < self.Count && self[atomIndex] is MathAtom placeholder &&
         placeholder?.AtomType is Enumerations.MathAtomType.Placeholder)
     {
         if (placeholder.Superscript is IMathList super)
         {
             if (atom.Superscript != null)
             {
                 super.Append(atom.Superscript);
             }
             atom.Superscript = super;
         }
         if (placeholder.Subscript is IMathList sub)
         {
             if (atom.Subscript != null)
             {
                 sub.Append(atom.Subscript);
             }
             atom.Subscript = sub;
         }
         self[atomIndex] = atom;
     }
Example #3
0
 internal bool ApplyModifier(string modifier, IMathAtom atom)
 {
     if (modifier == "limits")
     {
         if (atom is LargeOperator op)
         {
             op.Limits = true;
         }
         else
         {
             SetError(@"\limits can only be applied to an operator");
         }
         return(true);
     }
     else if (modifier == "nolimits")
     {
         if (atom is LargeOperator op)
         {
             op.Limits = false;
         }
         else
         {
             SetError(@"\nolimits can only be applied to an operator");
         }
         return(true);
     }
     return(false);
 }
Example #4
0
        public MathList(IMathList cloneMe, bool finalize) : this()
        {
            if (!finalize)
            {
                foreach (var atom in cloneMe.Atoms)
                {
                    var cloneAtom = AtomCloner.Clone(atom, finalize);
                    Add(cloneAtom);
                }
            }
            else
            {
                IMathAtom prevNode = null;
                foreach (var atom in cloneMe.Atoms)
                {
                    var newNode = AtomCloner.Clone(atom, finalize);
                    if (atom.IndexRange == Range.Zero)
                    {
                        int prevIndex = prevNode is null ? 0 : prevNode.IndexRange.Location + prevNode.IndexRange.Length;
                        newNode.IndexRange = new Range(prevIndex, 1);
                    }
                    switch (newNode.AtomType)
                    {
                    case MathAtomType.BinaryOperator:
                        switch (prevNode?.AtomType)
                        {
                        case null:
                        case MathAtomType.BinaryOperator:
                        case MathAtomType.Relation:
                        case MathAtomType.Open:
                        case MathAtomType.Punctuation:
                        case MathAtomType.LargeOperator:
                            newNode.AtomType = MathAtomType.UnaryOperator;
                            break;
                        }
                        break;

                    case MathAtomType.Relation:
                    case MathAtomType.Punctuation:
                    case MathAtomType.Close:
                        if (prevNode != null && prevNode.AtomType == MathAtomType.BinaryOperator)
                        {
                            prevNode.AtomType = MathAtomType.UnaryOperator;
                        }
                        break;

                    case MathAtomType.Number:
                        if (prevNode != null && prevNode.AtomType == MathAtomType.Number && prevNode.Subscript == null && prevNode.Superscript == null)
                        {
                            prevNode.Fuse(newNode);
                            continue; // do not add the new node; we fused it instead.
                        }
                        break;
                    }
                    Add(newNode);
                    prevNode = newNode;
                }
            }
        }
Example #5
0
        internal IMathAtom BuildTable(string environment, IMathList firstList, bool isRow, char stopChar)
        {
            var oldEnv = _currentEnvironment;

            _currentEnvironment = new TableEnvironmentProperties(environment);
            int currentRow                = 0;
            int currentColumn             = 0;
            List <List <IMathList> > rows = new List <List <IMathList> > {
                new List <IMathList>()
            };

            if (firstList != null)
            {
                rows[currentRow].Add(firstList);
                if (isRow)
                {
                    _currentEnvironment.NRows++;
                    currentRow++;
                    rows.Add(new List <IMathList>());
                }
                else
                {
                    currentColumn++;
                }
            }
            while (HasCharacters && !(_currentEnvironment.Ended))
            {
                var list = BuildInternal(false, stopChar);
                if (list == null)
                {
                    return(null);
                }
                rows[currentRow].Add(list);
                currentColumn++;
                if (_currentEnvironment.NRows > currentRow)
                {
                    currentRow = _currentEnvironment.NRows;
                    rows.Add(new List <IMathList>());
                    currentColumn = 0;
                }
            }
            if (_currentEnvironment.Name != null && !_currentEnvironment.Ended)
            {
                SetError(@"Missing \end");
                return(null);
            }
            IMathAtom table = MathAtoms.Table(_currentEnvironment.Name, rows, out string errorMessage);

            if (table == null && errorMessage != null)
            {
                SetError(errorMessage);
                return(null);
            }
            _currentEnvironment = oldEnv;
            return(table);
        }
Example #6
0
        private static bool IsNotBinaryOperator(IMathAtom prevNode)
        {
            if (prevNode == null)
            {
                return(true);
            }
            switch (prevNode.AtomType)
            {
            case MathAtomType.BinaryOperator:
            case MathAtomType.Relation:
            case MathAtomType.Open:
            case MathAtomType.Punctuation:
            case MathAtomType.LargeOperator:
                return(true);

            default:
                return(false);
            }
        }
Example #7
0
        public static string DelimiterToString(IMathAtom delimiter)
        {
            var command = MathAtoms.DelimiterName(delimiter);

            if (command == null)
            {
                return(string.Empty);
            }
            var singleChars = @"()[]<>|./";

            if (singleChars.IndexOf(command, StringComparison.OrdinalIgnoreCase) >= 0 && command.Length == 1)
            {
                return(command);
            }
            if (command == "||")
            {
                return(@"\|");
            }
            else
            {
                return(@"\" + command);
            }
        }
Example #8
0
 private void CheckAtomTypeAndNucleus(IMathAtom atom, MathAtomType type, string nucleus)
 {
     Assert.Equal(type, atom.AtomType);
     Assert.Equal(nucleus, atom.Nucleus);
 }
Example #9
0
 internal static void CheckClone(IMathAtom original, IMathAtom clone)
 {
     Assert.Equal(original, clone);
     Assert.False(ReferenceEquals(original, clone));
 }
Example #10
0
 public TextLineDisplay(List <TextRunDisplay <TFont, TGlyph> > runs, List <IMathAtom> atoms)
 {
     Runs  = runs;
     Atoms = new IMathAtom[atoms.Count];
     atoms.CopyTo(Atoms);
 }
Example #11
0
 public bool IsAtomAllowed(IMathAtom atom)
 {
     return(atom != null && atom.AtomType != MathAtomType.Boundary);
 }
Example #12
0
 internal static void CheckClone(IMathAtom original, IMathAtom clone)
 => MathListTest.CheckClone(original, clone);
Example #13
0
 public bool IsAtomAllowed(IMathAtom atom) => atom != null && atom.AtomType != MathAtomType.Boundary;
Example #14
0
        public static IMathAtom Table(
            string environment,
            List <List <IMathList> > rows,
            out string errorMessage)
        {
            errorMessage = null;
            var table = new Table(environment)
            {
                Cells = rows
            };
            IMathAtom r = null;

            if (environment != null && _matrixEnvironments.TryGetValue(environment, out var delimiters))
            {
                table.Environment        = "matrix"; // TableEnvironment is set to matrix as delimiters are converted to latex outside the table.
                table.InterColumnSpacing = 18;

                var style = new Style(LineStyle.Text);
                foreach (var row in table.Cells)
                {
                    foreach (var cell in row)
                    {
                        cell.Insert(0, style);
                    }
                }

                if (delimiters != null)
                {
                    var inner = new Inner {
                        LeftBoundary  = BoundaryAtom(delimiters.Value.First),
                        RightBoundary = BoundaryAtom(delimiters.Value.Second),
                        InnerList     = MathLists.WithAtoms(table)
                    };
                    r = inner;
                }
                else
                {
                    r = table;
                }
            }
            else if (environment == null)
            {
                table.InterRowAdditionalSpacing = 1;
                for (int i = 0; i < table.NColumns; i++)
                {
                    table.SetAlignment(ColumnAlignment.Left, i);
                }
                r = table;
            }
            else if (environment == "eqalign" || environment == "split" || environment == "aligned")
            {
                if (table.NColumns != 2)
                {
                    errorMessage = environment + " environment can have only 2 columns";
                }
                else
                {
                    // add a spacer before each of the second column elements, in order to create the correct spacing for "=" and other relations.
                    var spacer = Create(MathAtomType.Ordinary, string.Empty);
                    foreach (var row in table.Cells)
                    {
                        if (row.Count > 1)
                        {
                            row[1].Insert(0, spacer);
                        }
                    }
                    table.InterRowAdditionalSpacing = 1;
                    table.SetAlignment(ColumnAlignment.Right, 0);
                    table.SetAlignment(ColumnAlignment.Left, 1);
                    r = table;
                }
            }
            else if (environment == "displaylines" || environment == "gather")
            {
                if (table.NColumns != 1)
                {
                    errorMessage = environment + " environment can only have 1 column.";
                    return(null);
                }
                table.InterRowAdditionalSpacing = 1;
                table.InterColumnSpacing        = 0;
                table.SetAlignment(ColumnAlignment.Center, 0);
                r = table;
            }
            else if (environment == "eqnarray")
            {
                if (table.NColumns != 3)
                {
                    errorMessage = environment + " must have exactly 3 columns.";
                }
                else
                {
                    table.InterRowAdditionalSpacing = 1;
                    table.InterColumnSpacing        = 18;
                    table.SetAlignment(ColumnAlignment.Right, 0);
                    table.SetAlignment(ColumnAlignment.Center, 1);
                    table.SetAlignment(ColumnAlignment.Left, 2);
                    r = table;
                }
            }
            else if (environment == "cases")
            {
                if (table.NColumns < 1 || table.NColumns > 2)
                {
                    errorMessage = "cases environment must have 1 to 2 columns";
                }
                else
                {
                    table.InterColumnSpacing = 18;
                    table.SetAlignment(ColumnAlignment.Left, 0);
                    if (table.NColumns == 2)
                    {
                        table.SetAlignment(ColumnAlignment.Left, 1);
                    }
                    var style = new Style(LineStyle.Text);
                    foreach (var row in table.Cells)
                    {
                        foreach (var cell in row)
                        {
                            cell.Insert(0, style);
                        }
                    }
                    // add delimiters
                    var inner = new Inner {
                        LeftBoundary  = BoundaryAtom("{"),
                        RightBoundary = BoundaryAtom(".")
                    };
                    var space = ForLatexSymbolName(",");
                    inner.InnerList = MathLists.WithAtoms(space, table);
                    r = inner;
                }
            }
            return(r);
        }
Example #15
0
        internal IMathList BuildInternal(bool oneCharOnly, char stopChar)
        {
            if (oneCharOnly && stopChar > 0)
            {
                throw new InvalidOperationException("Cannot set both oneCharOnly and stopChar");
            }
            var       r        = new MathList();
            IMathAtom prevAtom = null;

            while (HasCharacters)
            {
                if (_error != null)
                {
                    return(null);
                }
                IMathAtom atom;
                var       ch = GetNextCharacter();
                if (oneCharOnly)
                {
                    if (ch == '^' || ch == '}' || ch == '_' || ch == '&')
                    {
                        // this is not the character we are looking for. They are for the caller to look at.
                        UnlookCharacter();
                        return(r);
                    }
                }
                if (stopChar > 0 && ch == stopChar)
                {
                    return(r);
                }
                switch (ch)
                {
                case '^':
                    if (prevAtom == null || prevAtom.Superscript != null || !prevAtom.ScriptsAllowed)
                    {
                        prevAtom = MathAtoms.Create(MathAtomType.Ordinary, string.Empty);
                        r.Add(prevAtom);
                    }
                    // this is a superscript for the previous atom.
                    // note, if the next char is StopChar, it will be consumed and doesn't count as stop.
                    prevAtom.Superscript = this.BuildInternal(true);
                    continue;

                case '_':
                    if (prevAtom == null || prevAtom.Subscript != null || !prevAtom.ScriptsAllowed)
                    {
                        prevAtom = MathAtoms.Create(MathAtomType.Ordinary, string.Empty);
                        r.Add(prevAtom);
                    }
                    // this is a subscript for the previous atom.
                    // note, if the next char is StopChar, it will be consumed and doesn't count as stop.
                    prevAtom.Subscript = this.BuildInternal(true);
                    continue;

                case '{':
                    IMathList sublist;
                    if (_currentEnvironment != null && _currentEnvironment.Name == null)
                    {
                        // \\ or \cr which do not have a corrosponding \end
                        var oldEnv = _currentEnvironment;
                        _currentEnvironment = null;
                        sublist             = BuildInternal(false, '}');
                        _currentEnvironment = oldEnv;
                    }
                    else
                    {
                        sublist = BuildInternal(false, '}');
                    }
                    if (sublist == null)
                    {
                        return(null);
                    }
                    prevAtom = sublist.Atoms.LastOrDefault();
                    if (oneCharOnly)
                    {
                        r.Append(sublist);
                        return(r);
                    }
#warning TODO Example
                    //https://phabricator.wikimedia.org/T99369
                    //https://phab.wmfusercontent.org/file/data/xsimlcnvo42siudvwuzk/PHID-FILE-bdcqexocj5b57tj2oezn/math_rendering.png
                    //dt, \text{d}t, \partial t, \nabla\psi \\ \underline\overline{dy/dx, \text{d}y/\text{d}x, \frac{dy}{dx}, \frac{\text{d}y}{\text{d}x}, \frac{\partial^2}{\partial x_1\partial x_2}y} \\ \prime,
                    atom = new Group {
                        InnerList = sublist
                    };
                    break;

                case '}':
                    if (oneCharOnly || stopChar != 0)
                    {
                        throw new InvalidCodePathException("This should have been handled before.");
                    }
                    SetError("Missing opening brace");
                    return(null);

                case '\\':
                    var command = ReadCommand();
                    var done    = StopCommand(command, r, stopChar);
                    if (done != null)
                    {
                        return(done);
                    }
                    if (_error != null)
                    {
                        return(null);
                    }
                    if (ApplyModifier(command, prevAtom))
                    {
                        continue;
                    }
                    var fontStyleQ = MathAtoms.FontStyle(command);
                    if (fontStyleQ.HasValue)
                    {
                        var fontStyle        = fontStyleQ.Value;
                        var oldSpacesAllowed = _textMode;
                        var oldFontStyle     = _currentFontStyle;
                        _textMode         = (command == "text");
                        _currentFontStyle = fontStyle;
                        var childList = BuildInternal(true);
                        if (childList == null)
                        {
                            return(null);
                        }
                        _currentFontStyle = oldFontStyle;
                        _textMode         = oldSpacesAllowed;
                        prevAtom          = childList.Atoms.LastOrDefault();
                        r.Append(childList);
                        if (oneCharOnly)
                        {
                            return(r);
                        }
                        continue;
                    }
                    atom = AtomForCommand(command, stopChar);
                    if (atom == null)
                    {
                        SetError(_error ?? "Internal error");
                        return(null);
                    }
                    break;

                case '&': { // column separation in tables
                    if (_currentEnvironment != null)
                    {
                        return(r);
                    }
                    var table = BuildTable(null, r, false, stopChar);
                    if (table == null)
                    {
                        return(null);
                    }
                    return(MathLists.WithAtoms(table));
                }

                case '\'': // this case is NOT in iosMath
                    int i = 1;
                    while (ExpectCharacter('\''))
                    {
                        i++;
                    }
                    atom = new Prime(i);
                    break;

                default:
                    if (_textMode && ch == ' ')
                    {
                        atom = MathAtoms.ForLatexSymbolName(" ");
                    }
                    else
                    {
                        atom = MathAtoms.ForCharacter(ch);
                        if (atom == null)
                        {
                            // not a recognized character
                            continue;
                        }
                    }
                    break;
                }
                if (atom == null)
                {
                    throw new InvalidCodePathException("Atom shouldn't be null");
                }
                atom.FontStyle = _currentFontStyle;
                r.Add(atom);
                prevAtom = atom;
                if (oneCharOnly)
                {
                    return(r); // we consumed our character.
                }
            }
            if (stopChar > 0)
            {
                if (stopChar == '}')
                {
                    SetError("Missing closing brace");
                }
                else
                {
                    // we never found our stop character.
                    SetError("Expected character not found: " + stopChar.ToString());
                }
            }
            return(r);
        }
Example #16
0
 public static string DelimiterName(IMathAtom boundaryAtom) =>
 boundaryAtom.AtomType == MathAtomType.Boundary &&
 BoundaryDelimiters.TryGetBySecond(boundaryAtom.Nucleus, out var name) ?
 name : null;
Example #17
0
        public static void Insert(this IMathList self, MathListIndex index, IMathAtom atom)
        {
            index = index ?? MathListIndex.Level0Index(0);
            if (index.AtomIndex > self.Atoms.Count)
            {
                throw new IndexOutOfRangeException($"Index {index.AtomIndex} is out of bounds for list of size {self.Atoms.Count}");
            }
            switch (index.SubIndexType)
            {
            case MathListSubIndexType.None:
                self.Insert(index.AtomIndex, atom);
                break;

            case MathListSubIndexType.Nucleus:
                var currentAtom = self.Atoms[index.AtomIndex];
                if (currentAtom.Subscript == null && currentAtom.Superscript == null)
                {
                    throw new SubIndexTypeMismatchException("Nuclear fusion is not supported if there are neither subscripts nor superscripts in the current atom.");
                }
                if (atom.Subscript != null || atom.Superscript != null)
                {
                    throw new ArgumentException("Cannot fuse with an atom that already has a subscript or a superscript");
                }
                atom.Subscript          = currentAtom.Subscript;
                atom.Superscript        = currentAtom.Superscript;
                currentAtom.Subscript   = null;
                currentAtom.Superscript = null;
                self.Insert(index.AtomIndex + index.SubIndex?.AtomIndex ?? 0, atom);
                break;

            case MathListSubIndexType.Degree:
            case MathListSubIndexType.Radicand:
                if (!(self.Atoms[index.AtomIndex] is Radical radical && radical.AtomType == Enumerations.MathAtomType.Radical))
                {
                    throw new SubIndexTypeMismatchException($"No radical found at index {index.AtomIndex}");
                }
                if (index.SubIndexType == MathListSubIndexType.Degree)
                {
                    radical.Degree.Insert(index.SubIndex, atom);
                }
                else
                {
                    radical.Radicand.Insert(index.SubIndex, atom);
                }
                break;

            case MathListSubIndexType.Numerator:
            case MathListSubIndexType.Denominator:
                if (!(self.Atoms[index.AtomIndex] is Fraction frac && frac.AtomType == Enumerations.MathAtomType.Fraction))
                {
                    throw new SubIndexTypeMismatchException($"No fraction found at index {index.AtomIndex}");
                }
                if (index.SubIndexType == MathListSubIndexType.Numerator)
                {
                    frac.Numerator.Insert(index.SubIndex, atom);
                }
                else
                {
                    frac.Denominator.Insert(index.SubIndex, atom);
                }
                break;

            case MathListSubIndexType.Subscript:
                var current = self.Atoms[index.AtomIndex];
                if (current.Subscript == null)
                {
                    throw new SubIndexTypeMismatchException($"No subscript for atom at index {index.AtomIndex}");
                }
                current.Subscript.Insert(index.SubIndex, atom);
                break;

            case MathListSubIndexType.Superscript:
                current = self.Atoms[index.AtomIndex];
                if (current.Superscript == null)
                {
                    throw new SubIndexTypeMismatchException($"No superscript for atom at index {index.AtomIndex}");
                }
                current.Superscript.Insert(index.SubIndex, atom);
                break;
            }
        }