Add support for \expandafter, \noexpand, \edef, \let, and \long#2122
Add support for \expandafter, \noexpand, \edef, \let, and \long#2122
Conversation
Codecov Report
@@ Coverage Diff @@
## master #2122 +/- ##
=======================================
Coverage 94.77% 94.77%
=======================================
Files 83 83
Lines 5358 5358
Branches 939 939
=======================================
Hits 5078 5078
Misses 257 257
Partials 23 23
Continue to review full report at Codecov.
|
df69352 to
5be7b5a
Compare
|
This is ready for review. |
|
Since this depends on #2138, maybe set the base of this PR to be |
a7416e8 to
638ead3
Compare
k4b7
left a comment
There was a problem hiding this comment.
Thanks for the PR. I'm familiar with these commands so most of my comments are questions. 😅 It would be good to have someone with more LaTeX knowledge review if possible. I do think that introducing some named constants for the different noexpand values would improve readability.
| `\gdef` and `\global\def` macros will persist between math expressions. | ||
| `\gdef`, `\xdef`, `\global\def`, `\global\edef`, `\global\let`, and `\global\futurelet` will persist between math expressions. | ||
|
|
||
| KaTeX has no `\par`, so all macros are long by default and `\long` will be ignored. |
src/Token.js
Outdated
| export class Token { | ||
| text: string; | ||
| loc: ?SourceLocation; | ||
| noexpand: ?number; /* 1: treat as \relax, 2: pass to the parser */ |
There was a problem hiding this comment.
Having constants for these would make the code clearer.
| // <assignment> -> <non-macro assignment>|<macro assignment> | ||
| // <non-macro assignment> -> <simple assignment>|\global<non-macro assignment> | ||
| // <macro assignment> -> <definition>|<prefix><macro assignment> | ||
| // <prefix> -> \global|\long|\outer |
There was a problem hiding this comment.
It's the (formal) grammar of TeX from the TeXbook
| names: [ | ||
| "\\global", "\\long", | ||
| "\\\\globallong", // can’t be entered directly | ||
| ], |
There was a problem hiding this comment.
Why are \global and \long grouped together?
There was a problem hiding this comment.
They work in the same manner as a prefix to the macro assignment.
src/functions/def.js
Outdated
| // if macro is undefined at this moment, set noexpand to 2 | ||
| // and unexpandable to not expand it later and pass to the parser |
There was a problem hiding this comment.
Is this to handle a case like \let\foo=\bar \def\bar{hello, world} \foo where the value of the let is defined after the let?
There was a problem hiding this comment.
This is to handle cases where non-macros are overwritten after \let, e.g., \let\foo=\frac \def\frac{123} \foo12.
| expect`\noexpand\foo y`.toParseLike("y", | ||
| new Settings({macros: {"\\foo": "x"}})); |
There was a problem hiding this comment.
It looks like the \foo is ignored in this case. Is that correct?
test/katex-spec.js
Outdated
| expect`\def\foo{a}\xdef\bar{\foo}\def\foo{}\bar`.toParseLike`a`; | ||
| expect`\def\foo{a}\xdef\bar{\def\noexpand\foo{}}\foo\bar\foo`.toParseLike`a`; | ||
| expect`\def\foo{a}\xdef\bar{\expandafter\foo\noexpand\foo}\def\foo{b}\bar` | ||
| .toParseLike`ab`; |
There was a problem hiding this comment.
I get the first one, but the second two are a bit mind warping.
| expect`\global\global\def\foo{}\foo`.toParseLike``; | ||
| expect`\global\long\def\foo{}\foo`.toParseLike``; |
There was a problem hiding this comment.
It looks like multiple global/long commands in a row are the same as having a single one of those commands, is that right?
| expect`\let\foo=\frac\def\frac{}\foo12`.toParseLike`\frac12`; | ||
| expect`\def\foo{1}\let\bar\foo\def\foo{2}\bar`.toParseLike`1`; | ||
| expect`\let\foo=\kern\edef\bar{\foo1em}\let\kern=\relax\bar`.toParseLike`\kern1em`; | ||
| expect`\let\foo{\frac\foo1}{2}`.toParseLike`\frac{1}{2}`; |
There was a problem hiding this comment.
I didn't realize the = was optional when using \let. In the first one it looks like \frac is being redefined, but the result doesn't include this redefinition. I think it would be useful to have comments for some of these tests to help people that aren't as familiar with the interections between let and def.
There was a problem hiding this comment.
I'll add comments on test cases.
| "input": "\\\\def\\\\bold{\\\\bgroup\\\\bf\\\\let\\\\next= }\\\\bold{a}", | ||
| "lastIndex": 40 |
There was a problem hiding this comment.
It's nice seeing the input here. It makes reviewing snapshot tests easier.
k4b7
left a comment
There was a problem hiding this comment.
LGTM. The comments were very helpful. Thank you!
| noexpand: ?boolean; // don't expand the token | ||
| treatAsRelax: ?boolean; // used in \noexpand |
There was a problem hiding this comment.
It's possible in theory, but there is no use yet.
I'm surprised that they are fairly simple to implement! Fixes #1413.