xaml-math icon indicating copy to clipboard operation
xaml-math copied to clipboard

Support for \begin and \end named environments

Open MillerMark opened this issue 5 years ago • 8 comments

Would be great if the engine/parser could support \begin and \end named environments.

Examples:

\begin{pmatrix} a_{11} & a_{12} \ a_{21} & a_{22} \end{pmatrix}

\begin{array}{c|c} 1 & 2 \ \hline 3 & 4 \end{array}

f(n) = \begin{cases} n/2 & \quad \text{if } n \text{ is even}\ -(n+1)/2 & \quad \text{if } n \text{ is odd} \end{cases}

\left(\begin{ array}{c} n \ r \end{array} \right) = \frac{n!}{r!(n-r)!}

TODO[F]

  • [x] pmatrix (see #329)
  • [ ] array
  • [ ] cases

MillerMark avatar Apr 02 '19 14:04 MillerMark

We now have all the necessary tools in 0.7.0 (mainly, the ICommandEnvironment interface), so it should be possible to implement in the current code base.

ForNeVeR avatar Sep 01 '19 07:09 ForNeVeR

Hello, any update on the support for this ?

fforjan avatar May 20 '21 13:05 fforjan

How would you implement this? I am not familiar with how the code works

rennmaus-coder avatar Dec 29 '22 21:12 rennmaus-coder

It all starts in TexFormulaParser::ProcessCommand. There's a huge switch over known commands, and a fallback to WpfMath.Parsers.StandardCommands::Dictionary.

I suggest that we need to add a new command named begin into this dictionary; the purpose of the command will be to invoke a child parser and read everything until it encounters a corresponding \end. The most comprehensive known parser is the MatrixCommandParser, so maybe you can model your new environment parser after it.

There's no ready code to solve the task "parse until a keyword is meet", but, if you take a look at WpfMath.TexFormulaParser::ReadElementGroup, it should be more or less straightforward.

There's always a catch, though. Here, the catch is nested environments. It's likely it's possible to construct a new ICommandEnvironment and inject an \end command into it, and then use that environment for nested parsing, in a way WpfMath.Parsers.Matrices.MatrixInternalEnvironment is used.

Though, I think if nested environment handling is too complex for now, we may consider restricting us to a single environment (no nesting) for start.

After you've done the task of getting a SourceSpan with the internal part between \begin and \end, everything else is more or less easy. Read the environment name from the first argument, and then, depending on the name, invoke nested formula parser by invoking WpfMath.Parsers.ICommandParser::ProcessCommand with the right arguments.

ForNeVeR avatar Dec 30 '22 17:12 ForNeVeR

Okay, thats what I have so far, I know it looks aweful

            public CommandProcessingResult ProcessCommand(CommandContext context)
            {
                int position = context.ArgumentsStartPosition;
                SourceSpan source = context.CommandSource;
                SourceSpan element = TexFormulaParser.ReadElement(source, ref position);
                string argument = element.ToString();
                int endIndex = -1;
                for (int i = position; position < source.End; i++)
                {
                    if (source[i] == '\\' && source[i + 1] == 'e' && source[i + 2] == 'n' && source[i + 3] == 'd')
                    {
                        endIndex = i;
                        break;
                    }
                }
                string newContent = source.ToString().Substring(context.ArgumentsStartPosition + element.Length + 2, endIndex - (context.ArgumentsStartPosition + element.Length) - 2);
                newContent = "\\" + argument + "{" + newContent + "}";
                SourceSpan innerContent = new SourceSpan("Begin-End-Environment", newContent, 0, newContent.Length);
                TexFormula content = context.Parser.Parse(innerContent);
                return new CommandProcessingResult(content.RootAtom, position + 3);
            }
        }

When I try to use this command \begin{pmatrix} a_1 & a_2 & a_3 \ b_1 & b_2 & b_3 \ c_1 & c_2 & c_3 \end{pmatrix} it says that '&' is unknown. However, when I copy the pmatrix from newContent and insert it in the TextBox, it works. Any ideas why?

rennmaus-coder avatar Jan 01 '23 17:01 rennmaus-coder

MatrixInternalEnvironment solves this problem by overriding the ProcessUnknownCharacter method: https://github.com/ForNeVeR/wpf-math/blob/10b9210b3108711e54bb480e8ed0485bd9c5cfc2/src/WpfMath/Parsers/Matrices/MatrixInternalEnvironment.cs#L27-L36

I believe that it doesn't work automatically because context.Parser.Parse(innerContent) doesn't receive the command name (it expects to have \pmatrix … to enter pmatrix-reading mode).

For beginning, you can try working that around by passing @"\pmatrix " + innerContent as an argument.

ForNeVeR avatar Jan 01 '23 20:01 ForNeVeR

This is newContent: \pmatrix{ a_1 & a_2 & a_3 \ b_1 & b_2 & b_3 \ c_1 & c_2 & c_3 } When I paste this in the TextBox it works, but when I parse it, it doesn't work

                string newContent = source.ToString().Substring(context.ArgumentsStartPosition + element.Length + 2, endIndex - (context.ArgumentsStartPosition + element.Length) - 2);
                newContent = "\\" + argument + "{" + newContent + "}";
                TexFormula content = context.Parser.Parse(newContent);
                return new CommandProcessingResult(content.RootAtom, position + 3);

content.RootAtom:

{FencedAtom { Source = pmatrix{ a_1 & a_2 & a_3 \\ b_1 & b_2 & b_3 \\ c_1 & c_2 & c_3  }, Type = Ordinary, BaseAtom = MatrixAtom { Source = pmatrix{ a_1 & a_2 & a_3 \\ b_1 & b_2 & b_3 \\ c_1 & c_2 & c_3  }, Type = Ordinary, MatrixCells = System.Collections.ObjectModel.ReadOnlyCollection'1[System.Collections.ObjectModel.ReadOnlyCollection'1[WpfMath.Atoms.Atom]], VerticalPadding = 0,35, HorizontalPadding = 0,35, MatrixCellAlignment = Center } }}

rennmaus-coder avatar Jan 02 '23 08:01 rennmaus-coder

I fixed it and created a new pr. Let me know if there is anything I should change

rennmaus-coder avatar Jan 02 '23 10:01 rennmaus-coder