-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement \TextOrMath, \@secondoftwo #1024
Conversation
* Parser now tells MacroExpander about mode switching. (This seems preferable to a circular reference between Parser and MacroExpander.) * Implement \TextOrMath * Improve when we switch modes so that this actually works, in all cases except single-symbol arguments. * Add \@secondoftwo to match \@FirstOfTwo. * Add comments documenting all the conditional macros
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work! I left a few comments and questions, but no major concerns.
src/MacroExpander.js
Outdated
/** | ||
* Switches between "text" and "math" modes. | ||
*/ | ||
switchMode(newMode) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
newMode: Mode
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.
const outerMode = this.mode; | ||
this.switchMode("math"); | ||
// Expand next symbol now that we're in math mode. | ||
this.consume(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This had to be moved so that the gullet is in the right mode before expansion, cool!
this.expect("$", true); | ||
// We can't expand the next symbol after the $ until after | ||
// switching modes back. So don't consume within expect. | ||
this.expect("$", false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent comment!
@@ -746,29 +752,25 @@ export default class Parser { | |||
* | |||
* @return {?ParsedFuncOrArgOrDollar} | |||
*/ | |||
parseGroupOfType(innerMode, optional) { | |||
const outerMode = this.mode; | |||
parseGroupOfType(type, optional) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice change
return res; | ||
// By the time we get here, type is one of "text" or "math". | ||
// Specify this as mode to parseGroup. | ||
return this.parseGroup(optional, type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'll be nice when we can guarantee this with flow
.
// Switch mode back before consuming symbol after close brace | ||
if (mode) { | ||
this.switchMode(outerMode); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this is why we have to pass in the mode
instead of calling switchMode
outside of parseGroup
.
src/Parser.js
Outdated
@@ -976,6 +989,7 @@ export default class Parser { | |||
firstToken.range(lastToken, firstToken.text)); | |||
} else { | |||
// Otherwise, just return a nucleus, or nothing for an optional group | |||
// TODO: Not yet switching modes for single-symbol group. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How much work is involved in this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not actually sure how to do it -- it's pretty challenging, because by the time parseGroup
gets called, nextToken
already has the single-symbol group, which means that it already got expanded in the wrong mode. In the old way of doing things, we could "unget" the symbol, change modes, and reget it, but that seems challenging/impossible to do if there are side-effects/errors caused by the first execution in the wrong mode. So I think we'd need to call parseGroup
before actually reading the first symbol of the group, which might cause a rather big change across all of Parser.js
.
So in summary, I'm not sure how much work it'll be. It's surely possible, but hard. I thought I'd commit what I have so far, and open another issue for fixing this further. On the plus side, it took me a lot of playing around with tests before I could even get this situation to arise, and in some sense #924 is even more important than this missing case. So maybe this PR is good to merge for now? Up to you -- I can look at this more when I have time.
@@ -2672,11 +2672,61 @@ describe("A macro expander", function() { | |||
expect("\\@ifnextchar!{yes}{no}?!").toParseLike("no?!"); | |||
}); | |||
|
|||
it("\\@firstoftwwo should consume star but nothing else", function() { | |||
it("\\@ifstar should consume star but nothing else", function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch
compareParseTree("\\mode\\text{\\mode$\\mode$\\mode}\\mode", | ||
"math\\text{text$math$text}math", | ||
{"\\mode": "\\TextOrMath{text}{math}"}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great test suite!
// which doesn't even treat all four letters as an argument. | ||
//it("\\TextOrMath should work in a macro passed to \\text", function() { | ||
// compareParseTree("\\text\\mode", "\\text{text}", | ||
// {"\\mode": "\\TextOrMath{text}{math}"}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this currently result in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It results in "m" formatted in text mode followed by "ath" formatted in math mode. So it's wrong in two ways: it thinks it's in math mode when it's actually in text mode, and the argument is just the first character instead of all four. This is a bug with interaction between MacroExpander
and Parser
, essentially #924 which I'll update.
Wow, the power of tests. DashesAndQuotes was a nice easy fix pointing out incorrect mode detection for the ligatures (obvious in hindsight). Unfortunately, the Colorbox issue is exactly the missing part of this PR, switching to text mode for a single-symbol argument (here, |
@edemaine the following seems to be sufficient to fix the
|
This fixes the Colorbox screenshot test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There may still be other places where we need to switch modes before calling |
@kevinbarabash Thanks for investigating! This partially fixes the single-symbol support, basically whenever the single symbol is not a macro (not expanded by the |
Partially addressing #1003.
\TextOrMath
\@secondoftwo
to match\@firstoftwo
.